Repository: dotnetprojects/WpfToolkit Branch: master Commit: dbe5005dd7b0 Files: 404 Total size: 9.8 MB Directory structure: gitextract_go_s1dlq/ ├── .editorconfig ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── DotNetProjects.snk ├── README.md ├── WpfToolkit/ │ ├── AssemblyAttrs.cs │ ├── AssemblyInfo.cs │ ├── Calendar/ │ │ ├── Microsoft/ │ │ │ └── Windows/ │ │ │ ├── Automation/ │ │ │ │ └── Peers/ │ │ │ │ ├── CalendarAutomationPeer.cs │ │ │ │ ├── CalendarButtonAutomationPeer.cs │ │ │ │ └── CalendarDayButtonAutomationPeer.cs │ │ │ └── Controls/ │ │ │ ├── Calendar.cs │ │ │ ├── CalendarBlackoutDatesCollection.cs │ │ │ ├── CalendarButton.cs │ │ │ ├── CalendarDateChangedEventArgs.cs │ │ │ ├── CalendarDateRange.cs │ │ │ ├── CalendarDateRangeChangingEventArgs.cs │ │ │ ├── CalendarDayButton.cs │ │ │ ├── CalendarItem.cs │ │ │ ├── CalendarMode.cs │ │ │ ├── CalendarModeChangedEventArgs.cs │ │ │ ├── CalendarSelectionChangedEventArgs.cs │ │ │ ├── CalendarSelectionMode.cs │ │ │ ├── DateTimeHelper.cs │ │ │ ├── KeyboardHelper.cs │ │ │ ├── SelectedDatesCollection.cs │ │ │ └── VisualStates.cs │ │ └── Themes/ │ │ ├── Aero.NormalColor.xaml │ │ ├── Classic.xaml │ │ ├── Generic.xaml │ │ ├── Luna.HomeStead.xaml │ │ ├── Luna.Metallic.xaml │ │ ├── Luna.NormalColor.xaml │ │ └── Royale.NormalColor.xaml │ ├── Common/ │ │ └── System/ │ │ └── Windows/ │ │ └── Controls/ │ │ ├── BindingEvaluator.cs │ │ ├── Extensions.cs │ │ ├── IUpdateVisualState.cs │ │ ├── InteractionHelper.cs │ │ ├── ItemsControlHelper.cs │ │ ├── VisualStates.cs │ │ ├── VisualTreeExtensions.cs │ │ └── WeakEventListener.cs │ ├── DataVisualization/ │ │ ├── AggregatedObservableCollection.cs │ │ ├── AssemblyInfoShared.cs │ │ ├── Charting/ │ │ │ ├── AnimationSequence.cs │ │ │ ├── Axis/ │ │ │ │ ├── Axis.cs │ │ │ │ ├── AxisIntervalType.cs │ │ │ │ ├── AxisLabel.cs │ │ │ │ ├── AxisLocation.cs │ │ │ │ ├── AxisOrientation.cs │ │ │ │ ├── CategoryAxis.cs │ │ │ │ ├── CategorySortOrder.cs │ │ │ │ ├── DateTimeAxis.cs │ │ │ │ ├── DateTimeAxisLabel.cs │ │ │ │ ├── DateTimeIntervalType.cs │ │ │ │ ├── DisplayAxis.cs │ │ │ │ ├── DisplayAxisGridLines.cs │ │ │ │ ├── IAnchoredToOrigin.cs │ │ │ │ ├── IAxis.cs │ │ │ │ ├── IAxisListener.cs │ │ │ │ ├── ICategoryAxis.cs │ │ │ │ ├── IDataConsumer.cs │ │ │ │ ├── IDataProvider.cs │ │ │ │ ├── IRangeAxis.cs │ │ │ │ ├── IRangeConsumer.cs │ │ │ │ ├── IRangeProvider.cs │ │ │ │ ├── IValueMarginConsumer.cs │ │ │ │ ├── IValueMarginProvider.cs │ │ │ │ ├── LinearAxis.cs │ │ │ │ ├── LogarithmicAxis.cs │ │ │ │ ├── NullableConverter.cs │ │ │ │ ├── NumericAxis.cs │ │ │ │ ├── NumericAxisLabel.cs │ │ │ │ ├── OrientedAxisGridLines.cs │ │ │ │ ├── RangeAxis.cs │ │ │ │ └── ValueMargin.cs │ │ │ ├── Chart/ │ │ │ │ ├── Chart.cs │ │ │ │ └── SeriesHostAxesCollection.cs │ │ │ ├── DataPoint/ │ │ │ │ ├── AreaDataPoint.cs │ │ │ │ ├── BarDataPoint.cs │ │ │ │ ├── BubbleDataPoint.cs │ │ │ │ ├── CandlestickDataPoint.cs │ │ │ │ ├── ColumnDataPoint.cs │ │ │ │ ├── DataPoint.cs │ │ │ │ ├── DataPointState.cs │ │ │ │ ├── LineDataPoint.cs │ │ │ │ ├── PieDataPoint.cs │ │ │ │ └── ScatterDataPoint.cs │ │ │ ├── FrameworkElementExtensions.cs │ │ │ ├── Helper/ │ │ │ │ ├── Converters.cs │ │ │ │ ├── FormattingConverter.cs │ │ │ │ └── TreeHelper.cs │ │ │ ├── IRequireSeriesHost.cs │ │ │ ├── ISeriesHost.cs │ │ │ ├── ISeriesHostExtensions.cs │ │ │ ├── Pie/ │ │ │ │ ├── PieChartHelper.cs │ │ │ │ ├── PieChartLabel.cs │ │ │ │ └── PieChartLabelArea.cs │ │ │ ├── Primitives/ │ │ │ │ ├── DelegatingListBox.cs │ │ │ │ ├── Edge.cs │ │ │ │ └── EdgePanel.cs │ │ │ ├── ResourceDictionaryDispensedEventArgs.cs │ │ │ ├── ResourceDictionaryDispenser.cs │ │ │ ├── ResourceDictionaryEnumerator.cs │ │ │ ├── Series/ │ │ │ │ ├── AreaSeries.cs │ │ │ │ ├── BarSeries.cs │ │ │ │ ├── BubbleSeries.cs │ │ │ │ ├── CandlestickSeries.cs │ │ │ │ ├── ColumnBarBaseSeries.cs │ │ │ │ ├── ColumnSeries.cs │ │ │ │ ├── Compatible/ │ │ │ │ │ ├── AreaSeries.cs │ │ │ │ │ ├── BarSeries.cs │ │ │ │ │ ├── ColumnSeries.cs │ │ │ │ │ ├── LineSeries.cs │ │ │ │ │ ├── ScatterSeries.cs │ │ │ │ │ └── SelectionEnabledToSelectionModeConverter.cs │ │ │ │ ├── DataPointSeries.cs │ │ │ │ ├── DataPointSeriesWithAxes.cs │ │ │ │ ├── DataPointSingleSeriesWithAxes.cs │ │ │ │ ├── DefinitionSeries.cs │ │ │ │ ├── IRequireGlobalSeriesIndex.cs │ │ │ │ ├── ISeries.cs │ │ │ │ ├── LabeledPieSeries.cs │ │ │ │ ├── LegendItem.cs │ │ │ │ ├── LineAreaBaseSeries.cs │ │ │ │ ├── LineSeries.cs │ │ │ │ ├── PieSeries.cs │ │ │ │ ├── ScatterSeries.cs │ │ │ │ ├── Series.cs │ │ │ │ ├── SeriesDefinition.cs │ │ │ │ ├── SeriesSelectionMode.cs │ │ │ │ ├── SplineSeries.cs │ │ │ │ ├── Stacked100AreaSeries.cs │ │ │ │ ├── Stacked100BarSeries.cs │ │ │ │ ├── Stacked100ColumnSeries.cs │ │ │ │ ├── Stacked100LineSeries.cs │ │ │ │ ├── StackedAreaLineSeries.cs │ │ │ │ ├── StackedAreaSeries.cs │ │ │ │ ├── StackedBarColumnSeries.cs │ │ │ │ ├── StackedBarSeries.cs │ │ │ │ ├── StackedColumnSeries.cs │ │ │ │ └── StackedLineSeries.cs │ │ │ └── ValueMarginCoordinateAndOverlap.cs │ │ ├── Collections/ │ │ │ ├── LeftLeaningRedBlackTree.cs │ │ │ ├── MultipleDictionary.cs │ │ │ └── OrderedMultipleDictionary.cs │ │ ├── ColorExtensions.cs │ │ ├── DependencyPropertyAnimationHelper.cs │ │ ├── DesignerProperties.cs │ │ ├── DotNetProjects.DataVisualization.Toolkit.csproj │ │ ├── DotNetProjects.snk │ │ ├── EnumerableExtensions.cs │ │ ├── EnumerableFunctions.cs │ │ ├── GenericEqualityComparer.cs │ │ ├── GlobalSuppressions.cs │ │ ├── GridExtensions.cs │ │ ├── IResourceDictionaryDispenser.cs │ │ ├── LayoutTransformControl.cs │ │ ├── Legend/ │ │ │ └── Legend.cs │ │ ├── NoResetObservableCollection.cs │ │ ├── ObjectPool.cs │ │ ├── ObservableCollectionListAdapter.cs │ │ ├── OrientedPanel.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── Range.cs │ │ ├── RangeEnumerableFunctions.cs │ │ ├── ReadOnlyObservableCollection.cs │ │ ├── ResourceDictionaryCollection.cs │ │ ├── StoryboardQueue.cs │ │ ├── StringFormatConverter.cs │ │ ├── Themes/ │ │ │ └── generic.xaml │ │ ├── Title/ │ │ │ └── Title.cs │ │ ├── TreeMap/ │ │ │ ├── BindingExtractor.cs │ │ │ ├── Interpolators/ │ │ │ │ ├── DoubleInterpolator.cs │ │ │ │ ├── HSLSolidColorBrushInterpolator.cs │ │ │ │ ├── InterpolationMode.cs │ │ │ │ ├── Interpolator.cs │ │ │ │ ├── RangeInterpolator.cs │ │ │ │ └── SolidColorBrushInterpolator.cs │ │ │ ├── Layout/ │ │ │ │ ├── SquaringAlgorithm.cs │ │ │ │ └── TreeMapNode.cs │ │ │ ├── TreeMap.cs │ │ │ ├── TreeMapItemDefinition.cs │ │ │ └── TreeMapItemDefinitionSelector.cs │ │ ├── Tuple.cs │ │ ├── UniqueObservableCollection.cs │ │ ├── Unit.cs │ │ ├── UnitValue.cs │ │ ├── ValueHelper.cs │ │ ├── WeakEventListener.cs │ │ └── WeakReferenceBag.cs │ ├── DatePicker/ │ │ ├── Microsoft/ │ │ │ └── Windows/ │ │ │ ├── Automation/ │ │ │ │ └── Peers/ │ │ │ │ └── DatePickerAutomationPeer.cs │ │ │ └── Controls/ │ │ │ ├── DatePicker.cs │ │ │ ├── DatePickerDateValidationErrorEventArgs.cs │ │ │ ├── DatePickerFormat.cs │ │ │ └── DatePickerTextBox.cs │ │ └── Themes/ │ │ ├── Aero.NormalColor.xaml │ │ ├── Classic.xaml │ │ ├── Generic.xaml │ │ ├── Luna.HomeStead.xaml │ │ ├── Luna.Metallic.xaml │ │ ├── Luna.NormalColor.xaml │ │ └── Royale.NormalColor.xaml │ ├── GlobalSuppressions.cs │ ├── Input/ │ │ ├── AutoCompleteBox/ │ │ │ └── System/ │ │ │ └── Windows/ │ │ │ ├── Automation/ │ │ │ │ └── Peers/ │ │ │ │ └── AutoCompleteBoxAutomationPeer.cs │ │ │ └── Controls/ │ │ │ ├── AutoCompleteBox.cs │ │ │ ├── AutoCompleteFilter.cs │ │ │ ├── AutoCompleteFilterMode.cs │ │ │ ├── AutoCompleteFilterPredicate.cs │ │ │ ├── ISelectionAdapter.cs │ │ │ ├── ItemsControlExtensions.cs │ │ │ ├── PopulatedEventArgs.cs │ │ │ ├── PopulatedEventHandler.cs │ │ │ ├── PopulatingEventArgs.cs │ │ │ ├── PopulatingEventHandler.cs │ │ │ ├── PopupHelper.cs │ │ │ ├── RoutedPropertyChangingEventArgs.cs │ │ │ ├── RoutedPropertyChangingEventHandler.cs │ │ │ ├── SelectorSelectionAdapter.cs │ │ │ └── ValueByStringHelper.cs │ │ ├── DotNetProjects.Input.Toolkit.csproj │ │ ├── DotNetProjects.Input.Toolkit.csproj.DotSettings │ │ ├── DotNetProjects.snk │ │ ├── GlobalSuppressions.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── Rating/ │ │ │ └── System/ │ │ │ └── Windows/ │ │ │ ├── Automation/ │ │ │ │ └── Peers/ │ │ │ │ ├── RatingAutomationPeer.cs │ │ │ │ └── RatingItemAutomationPeer.cs │ │ │ └── Controls/ │ │ │ ├── Clipper.cs │ │ │ ├── EnumerableFunctions.cs │ │ │ ├── LinearClipper.cs │ │ │ ├── NullableConverter.cs │ │ │ ├── Rating.cs │ │ │ ├── RatingItem.cs │ │ │ ├── RatingSelectionMode.cs │ │ │ ├── Tuple.cs │ │ │ └── TupleExtensions.cs │ │ └── Themes/ │ │ └── Generic.xaml │ ├── Layout/ │ │ ├── Accordion/ │ │ │ └── System/ │ │ │ └── Windows/ │ │ │ ├── Automation/ │ │ │ │ └── Peers/ │ │ │ │ ├── AccordionAutomationPeer.cs │ │ │ │ ├── AccordionItemAutomationPeer.cs │ │ │ │ └── AccordionItemWrapperAutomationPeer.cs │ │ │ └── Controls/ │ │ │ ├── Accordion.cs │ │ │ ├── AccordionAction.cs │ │ │ ├── AccordionButton.cs │ │ │ ├── AccordionItem.cs │ │ │ ├── AccordionSelectionMode.cs │ │ │ ├── ExpandableContentControl.cs │ │ │ └── SelectionSequence.cs │ │ ├── DotNetProjects.Layout.Toolkit.csproj │ │ ├── DotNetProjects.snk │ │ ├── Extensions/ │ │ │ ├── MathExtensions.cs │ │ │ └── RectExtensions.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── System.Windows.Controls.Layout.Toolkit.XML │ │ ├── Themes/ │ │ │ └── Generic.xaml │ │ ├── TransitioningContentControl/ │ │ │ └── System/ │ │ │ └── Windows/ │ │ │ └── Controls/ │ │ │ └── TransitioningContentControl.cs │ │ └── ZoomableCanvas/ │ │ ├── LinkedListExtensions.cs │ │ ├── PriorityQuadTree.cs │ │ ├── PriorityQueue.cs │ │ ├── VirtualPanel.cs │ │ └── ZoomableCanvas.cs │ ├── Resources/ │ │ ├── ExceptionStringTable.txt │ │ ├── SR.cs │ │ └── SRID.cs │ ├── Samples/ │ │ ├── Accordion/ │ │ │ ├── AccordionSample.xaml │ │ │ ├── AccordionSample.xaml.cs │ │ │ ├── AccordionShowcase.xaml │ │ │ ├── AccordionShowcase.xaml.cs │ │ │ ├── AccordionUsage.xaml │ │ │ └── AccordionUsage.xaml.cs │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── AutoCompleteBox/ │ │ │ ├── AutoCompleteBoxSample.xaml │ │ │ ├── AutoCompleteBoxSample.xaml.cs │ │ │ ├── AutoCompleteComboBox.xaml │ │ │ ├── AutoCompleteComboBox.xaml.cs │ │ │ ├── AutoCompleteLambda.xaml │ │ │ ├── AutoCompleteLambda.xaml.cs │ │ │ ├── CustomEvents.xaml │ │ │ ├── CustomEvents.xaml.cs │ │ │ ├── DataGridAutoCompleteBox.xaml │ │ │ ├── DataGridAutoCompleteBox.xaml.cs │ │ │ ├── DataGridAutoCompleteBoxEdit.xaml │ │ │ ├── DataGridAutoCompleteBoxEdit.xaml.cs │ │ │ ├── DataGridSelectionAdapter.cs │ │ │ ├── DictionaryKeyValueConverter.cs │ │ │ ├── RandomEmployeeDetails.cs │ │ │ ├── SampleEmployeeCollection.cs │ │ │ ├── SearchSuggestionSample.xaml │ │ │ └── SearchSuggestionSample.xaml.cs │ │ ├── BlogPostData.xml │ │ ├── Common/ │ │ │ ├── BusinessObjects/ │ │ │ │ ├── Airport.cs │ │ │ │ ├── City.cs │ │ │ │ ├── Contact.cs │ │ │ │ ├── Customer.cs │ │ │ │ ├── CustomerList.cs │ │ │ │ ├── Department.cs │ │ │ │ ├── Employee.cs │ │ │ │ ├── Feature.cs │ │ │ │ ├── MemberInfoData.cs │ │ │ │ ├── ObjectCollection.cs │ │ │ │ ├── Photograph.cs │ │ │ │ └── Words.cs │ │ │ ├── GridLengthAnimation.cs │ │ │ ├── SharedResources.cs │ │ │ └── WebServiceHelper.cs │ │ ├── DataVisualisation/ │ │ │ ├── DataVisualisationSample.xaml │ │ │ ├── DataVisualisationSample.xaml.cs │ │ │ └── SampleDataVM.cs │ │ ├── MainWindow.xaml │ │ ├── MainWindow.xaml.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ ├── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ └── Settings.settings │ │ ├── Rating/ │ │ │ ├── RatingSample.xaml │ │ │ └── RatingSample.xaml.cs │ │ ├── Themes/ │ │ │ ├── ThemesExample.xaml │ │ │ └── ThemesExample.xaml.cs │ │ ├── TreeMap/ │ │ │ ├── BlogPost.cs │ │ │ ├── TreeMapIntroduction.xaml │ │ │ └── TreeMapIntroduction.xaml.cs │ │ ├── WPFToolkitSamples.csproj │ │ └── app.config │ ├── Themes/ │ │ ├── Aero.NormalColor.xaml │ │ ├── Classic.xaml │ │ ├── Generic.xaml │ │ ├── Luna.HomeStead.xaml │ │ ├── Luna.Metallic.xaml │ │ ├── Luna.NormalColor.xaml │ │ └── Royale.NormalColor.xaml │ ├── WPF.Themes/ │ │ ├── BubbleCreme/ │ │ │ └── Theme.xaml │ │ ├── BureauBlack/ │ │ │ └── Theme.xaml │ │ ├── BureauBlue/ │ │ │ └── Theme.xaml │ │ ├── DavesGlossyControls/ │ │ │ └── Theme.xaml │ │ ├── DotNetProjects.WPF.Themes.csproj │ │ ├── DotNetProjects.snk │ │ ├── ExpressionDark/ │ │ │ └── Theme.xaml │ │ ├── ExpressionLight/ │ │ │ └── Theme.xaml │ │ ├── IG/ │ │ │ ├── IG.MSControls.Toolkit.Implicit.xaml │ │ │ ├── Styles.Shared.xaml │ │ │ ├── Styles.WPF.xaml │ │ │ ├── Theme.Colors.xaml │ │ │ └── Theme.xaml │ │ ├── JetPack/ │ │ │ ├── Assets/ │ │ │ │ ├── Brushes.xaml │ │ │ │ ├── CoreStyles.xaml │ │ │ │ ├── Fonts.xaml │ │ │ │ ├── SdkStyles.xaml │ │ │ │ └── Styles.xaml │ │ │ ├── Helpers/ │ │ │ │ ├── DataGridCheckBoxColumnFixer.cs │ │ │ │ └── GridLengthAnimation.cs │ │ │ └── Theme.xaml │ │ ├── MetroDark/ │ │ │ ├── MetroDark.MSControls.Toolkit.Implicit.xaml │ │ │ ├── Styles.Shared.xaml │ │ │ ├── Styles.WPF.xaml │ │ │ ├── Theme.Colors.xaml │ │ │ └── Theme.xaml │ │ ├── MetroLight/ │ │ │ ├── Metro.MSControls.Toolkit.Implicit.xaml │ │ │ ├── Styles.Shared.xaml │ │ │ ├── Styles.WPF.xaml │ │ │ ├── Theme.Colors.xaml │ │ │ └── Theme.xaml │ │ ├── MoonUICore/ │ │ │ └── Theme.xaml │ │ ├── Office2010Blue/ │ │ │ ├── Office2010Blue.MSControls.Toolkit.Implicit.xaml │ │ │ ├── Styles.Shared.xaml │ │ │ ├── Styles.WPF.xaml │ │ │ ├── Theme.Colors.xaml │ │ │ └── Theme.xaml │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ ├── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ └── Settings.settings │ │ ├── RainierOrange/ │ │ │ └── Theme.xaml │ │ ├── RainierPurple/ │ │ │ └── Theme.xaml │ │ ├── RainierRadialBlue/ │ │ │ └── Theme.xaml │ │ ├── ShinyBlue/ │ │ │ └── Theme.xaml │ │ ├── ShinyDarkGreen/ │ │ │ └── Theme.xaml │ │ ├── ShinyDarkPurple/ │ │ │ └── Theme.xaml │ │ ├── ShinyDarkTeal/ │ │ │ └── Theme.xaml │ │ ├── ShinyRed/ │ │ │ └── Theme.xaml │ │ ├── SunnyOrange/ │ │ │ └── Theme.xaml │ │ ├── ThemeManager.cs │ │ ├── TwilightBlue/ │ │ │ └── Theme.xaml │ │ ├── UXMusingsBubblyBlue/ │ │ │ └── Theme.xaml │ │ ├── UXMusingsGreen/ │ │ │ └── Theme.xaml │ │ ├── UXMusingsRed/ │ │ │ └── Theme.xaml │ │ ├── UXMusingsRoughGreen/ │ │ │ └── Theme.xaml │ │ ├── UXMusingsRoughRed/ │ │ │ └── Theme.xaml │ │ └── WhistlerBlue/ │ │ └── Theme.xaml │ ├── WPFToolkit.sln │ └── WpfTest/ │ ├── App.xaml │ ├── App.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ └── WpfTest.csproj ├── appveyor.yml └── build.bat ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] end_of_line = crlf insert_final_newline = true indent_style = space indent_size = 4 ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: jogibear9988 patreon: jogibear9988 ================================================ FILE: .gitignore ================================================ # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) [Bb]in/ [Oo]bj/ # mstest test results TestResults ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files .vs *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.log *.vspscc *.vssscc .builds # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper* # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish # Publish Web Output *.Publish.xml # NuGet Packages Directory packages *.nupkg package/lib/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others [Bb]in [Oo]bj sql TestResults [Tt]est[Rr]esult* *.Cache ClientBin [Ss]tyle[Cc]op.* ~$* *.dbmdl Generated_Code #added for RIA/Silverlight projects # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML ================================================ FILE: README.md ================================================ DotNetProjects.Wpf.Toolkit ========================== News ---- !!NetCore 3 support!! Nuget ----- A Fork of the MS Wpf Toolkit, supporting NetCore3, splited in 4 Nugets: https://www.nuget.org/packages/DotNetProjects.WpfToolkit.Input/ https://www.nuget.org/packages/DotNetProjects.WpfToolkit.Layout/ https://www.nuget.org/packages/DotNetProjects.WpfToolkit.DataVisualization/ https://www.nuget.org/packages/DotNetProjects.WpfToolkit.Themes/ Be careful! The old nuget: https://www.nuget.org/packages/DotNetProjects.Wpf.Toolkit/ is deprecated And these 4 ones: https://www.nuget.org/packages/DotNetProjects.Input.Toolkit/ https://www.nuget.org/packages/DotNetProjects.Layout.Toolkit/ https://www.nuget.org/packages/DotNetProjects.DataVisualization.Toolkit/ https://www.nuget.org/packages/DotNetProjects.WPF.Themes/ are renamed. Info ---- wpf toolkit fork of the MS WPF Toolkit (https://wpf.codeplex.com/releases/view/40535) Additional Features: - Port to .NET 4.0 - Remove of Controls which exist already in .NET 4.0! - Create Example for the Charting Controls - Include of the WPF Theming Project - splited into 4 nugets - Work on Bugfixes (if someone knows some) ================================================ FILE: WpfToolkit/AssemblyAttrs.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file specifies various assembly level attributes. // //--------------------------------------------------------------------------- using System; using System.Resources; using System.Security; using System.Windows; using System.Windows.Markup; // Needed to turn on checking of security critical call chains [assembly:SecurityCritical] // Needed to enable xbap scenarios [assembly:AllowPartiallyTrustedCallers] [assembly:CLSCompliant(true)] [assembly:NeutralResourcesLanguage("en-US")] [assembly:ThemeInfo( // Specifies the location of theme specific resources ResourceDictionaryLocation.SourceAssembly, // Specifies the location of non-theme specific resources: ResourceDictionaryLocation.SourceAssembly)] [assembly:XmlnsDefinition("http://schemas.microsoft.com/wpf/2008/toolkit", "Microsoft.Windows.Controls")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/wpf/2008/toolkit", "Microsoft.Windows.Controls.Primitives")] // This line adds the public classes in this assembly and the System.Windows namespace to // the default WPF namespace. This makes it XAML compatible with Silverlight where VisualStateManager // is part of the default namespace. [assembly:XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows")] ================================================ FILE: WpfToolkit/AssemblyInfo.cs ================================================ using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("WPF Toolkit")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft Corp.")] [assembly: AssemblyProduct("WPF Toolkit")] [assembly: AssemblyCopyright("Copyright © 2010 Microsoft Corp. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // In order to begin building localizable applications, set // CultureYouAreCodingWith in your .csproj file // inside a . For example, if you are using US english // in your source files, set the to en-US. Then uncomment // the NeutralResourceLanguage attribute below. Update the "en-US" in // the line below to match the UICulture setting in the project file. // [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("5.0.*")] [assembly: AssemblyFileVersion("5.0.*")] ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Automation/Peers/CalendarAutomationPeer.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls; using Microsoft.Windows.Controls; using Microsoft.Windows.Controls.Primitives; namespace Microsoft.Windows.Automation.Peers { /// /// AutomationPeer for Calendar Control /// public sealed class CalendarAutomationPeer : FrameworkElementAutomationPeer, IGridProvider, IMultipleViewProvider, ISelectionProvider, ITableProvider { /// /// Initializes a new instance of the CalendarAutomationPeer class. /// /// Owning Calendar public CalendarAutomationPeer(Calendar owner) : base(owner) { } #region Private Properties private Calendar OwningCalendar { get { return this.Owner as Calendar; } } private Grid OwningGrid { get { if (this.OwningCalendar != null && this.OwningCalendar.MonthControl != null) { if (this.OwningCalendar.DisplayMode == CalendarMode.Month) { return this.OwningCalendar.MonthControl.MonthView; } else { return this.OwningCalendar.MonthControl.YearView; } } return null; } } #endregion Private Properties #region Public Methods /// /// Gets the control pattern that is associated with the specified System.Windows.Automation.Peers.PatternInterface. /// /// A value from the System.Windows.Automation.Peers.PatternInterface enumeration. /// The object that supports the specified pattern, or null if unsupported. public override object GetPattern(PatternInterface patternInterface) { switch (patternInterface) { case PatternInterface.Grid: case PatternInterface.Table: case PatternInterface.MultipleView: case PatternInterface.Selection: { if (this.OwningGrid != null) { return this; } break; } default: break; } return base.GetPattern(patternInterface); } #endregion Public Methods #region Protected Methods /// /// Gets the control type for the element that is associated with the UI Automation peer. /// /// The control type. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Calendar; } /// /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, /// differentiates the control represented by this AutomationPeer. /// /// The string that contains the name. protected override string GetClassNameCore() { return this.Owner.GetType().Name; } #endregion Protected Methods #region InternalMethods internal void RaiseSelectionEvents(SelectionChangedEventArgs e) { int numSelected = this.OwningCalendar.SelectedDates.Count; int numAdded = e.AddedItems.Count; if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) && numSelected == 1 && numAdded == 1) { CalendarDayButton selectedButton = this.OwningCalendar.FindDayButtonFromDay((DateTime)e.AddedItems[0]); if (selectedButton != null) { AutomationPeer peer = FrameworkElementAutomationPeer.FromElement(selectedButton); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected); } } } else { if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection)) { foreach (DateTime date in e.AddedItems) { CalendarDayButton selectedButton = this.OwningCalendar.FindDayButtonFromDay(date); if (selectedButton != null) { AutomationPeer peer = FrameworkElementAutomationPeer.FromElement(selectedButton); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementAddedToSelection); } } } } if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) { foreach (DateTime date in e.RemovedItems) { CalendarDayButton removedButton = this.OwningCalendar.FindDayButtonFromDay(date); if (removedButton != null) { AutomationPeer peer = FrameworkElementAutomationPeer.FromElement(removedButton); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection); } } } } } } #endregion InternalMethods #region IGridProvider int IGridProvider.ColumnCount { get { if (this.OwningGrid != null) { return this.OwningGrid.ColumnDefinitions.Count; } return 0; } } int IGridProvider.RowCount { get { if (this.OwningGrid != null) { if (this.OwningCalendar.DisplayMode == CalendarMode.Month) { // In Month DisplayMode, since first row is DayTitles, we return the RowCount-1 return Math.Max(0, this.OwningGrid.RowDefinitions.Count - 1); } else { return this.OwningGrid.RowDefinitions.Count; } } return 0; } } IRawElementProviderSimple IGridProvider.GetItem(int row, int column) { if (this.OwningCalendar.DisplayMode == CalendarMode.Month) { // In Month DisplayMode, since first row is DayTitles, we increment the row number by 1 row++; } if (this.OwningGrid != null && row >= 0 && row < this.OwningGrid.RowDefinitions.Count && column >= 0 && column < this.OwningGrid.ColumnDefinitions.Count) { foreach (UIElement child in this.OwningGrid.Children) { int childRow = (int)child.GetValue(Grid.RowProperty); int childColumn = (int)child.GetValue(Grid.ColumnProperty); if (childRow == row && childColumn == column) { AutomationPeer peer = CreatePeerForElement(child); if (peer != null) { return ProviderFromPeer(peer); } } } } return null; } #endregion IGridProvider #region IMultipleViewProvider int IMultipleViewProvider.CurrentView { get { return (int)this.OwningCalendar.DisplayMode; } } int[] IMultipleViewProvider.GetSupportedViews() { int[] supportedViews = new int[3]; supportedViews[0] = (int)CalendarMode.Month; supportedViews[1] = (int)CalendarMode.Year; supportedViews[2] = (int)CalendarMode.Decade; return supportedViews; } string IMultipleViewProvider.GetViewName(int viewId) { switch (viewId) { case 0: { return SR.Get(SRID.CalendarAutomationPeer_MonthMode); } case 1: { return SR.Get(SRID.CalendarAutomationPeer_YearMode); } case 2: { return SR.Get(SRID.CalendarAutomationPeer_DecadeMode); } } // TODO: update when Jolt 23302 is fixed // throw new ArgumentOutOfRangeException("viewId", Resource.Calendar_OnDisplayModePropertyChanged_InvalidValue); return String.Empty; } void IMultipleViewProvider.SetCurrentView(int viewId) { this.OwningCalendar.DisplayMode = (CalendarMode)viewId; } #endregion IMultipleViewProvider #region ISelectionProvider bool ISelectionProvider.CanSelectMultiple { get { return this.OwningCalendar.SelectionMode == CalendarSelectionMode.SingleRange || this.OwningCalendar.SelectionMode == CalendarSelectionMode.MultipleRange; } } bool ISelectionProvider.IsSelectionRequired { get { return false; } } IRawElementProviderSimple[] ISelectionProvider.GetSelection() { List providers = new List(); if (this.OwningGrid != null) { if (this.OwningCalendar.DisplayMode == CalendarMode.Month && this.OwningCalendar.SelectedDates != null && this.OwningCalendar.SelectedDates.Count != 0) { // return selected DayButtons CalendarDayButton dayButton; foreach (UIElement child in this.OwningGrid.Children) { int childRow = (int)child.GetValue(Grid.RowProperty); if (childRow != 0) { dayButton = child as CalendarDayButton; if (dayButton != null && dayButton.IsSelected) { AutomationPeer peer = CreatePeerForElement(dayButton); if (peer != null) { providers.Add(ProviderFromPeer(peer)); } } } } } else { // return the CalendarButton which has focus CalendarButton calendarButton; foreach (UIElement child in this.OwningGrid.Children) { calendarButton = child as CalendarButton; if (calendarButton != null && calendarButton.IsFocused) { AutomationPeer peer = CreatePeerForElement(calendarButton); if (peer != null) { providers.Add(ProviderFromPeer(peer)); } break; } } } if (providers.Count > 0) { return providers.ToArray(); } } return null; } #endregion ISelectionProvider #region ITableProvider RowOrColumnMajor ITableProvider.RowOrColumnMajor { get { return RowOrColumnMajor.RowMajor; } } IRawElementProviderSimple[] ITableProvider.GetColumnHeaders() { if (this.OwningCalendar.DisplayMode == CalendarMode.Month) { List providers = new List(); foreach (UIElement child in this.OwningGrid.Children) { int childRow = (int)child.GetValue(Grid.RowProperty); if (childRow == 0) { AutomationPeer peer = CreatePeerForElement(child); if (peer != null) { providers.Add(ProviderFromPeer(peer)); } } } if (providers.Count > 0) { return providers.ToArray(); } } return null; } // If WeekNumber functionality is supported by Calendar in the future, // this method should return weeknumbers IRawElementProviderSimple[] ITableProvider.GetRowHeaders() { return null; } #endregion ITableProvider } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Automation/Peers/CalendarButtonAutomationPeer.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls; using System.Windows.Input; using Microsoft.Windows.Controls; using Microsoft.Windows.Controls.Primitives; namespace Microsoft.Windows.Automation.Peers { /// /// AutomationPeer for CalendarButton /// public sealed class CalendarButtonAutomationPeer : ButtonAutomationPeer, IGridItemProvider, ISelectionItemProvider { /// /// Initializes a new instance of the CalendarButtonAutomationPeer class. /// /// Owning CalendarButton public CalendarButtonAutomationPeer(CalendarButton owner) : base(owner) { } #region Private Properties private Calendar OwningCalendar { get { return this.OwningCalendarButton.Owner; } } private IRawElementProviderSimple OwningCalendarAutomationPeer { get { if (this.OwningCalendar != null) { AutomationPeer peer = CreatePeerForElement(this.OwningCalendar); if (peer != null) { return ProviderFromPeer(peer); } } return null; } } private CalendarButton OwningCalendarButton { get { return this.Owner as CalendarButton; } } private DateTime? Date { get { if (this.OwningCalendarButton != null && this.OwningCalendarButton.DataContext is DateTime) { return (DateTime?)this.OwningCalendarButton.DataContext; } else { return null; } } } #endregion Private Properties #region Public Methods /// /// Gets the control pattern that is associated with the specified System.Windows.Automation.Peers.PatternInterface. /// /// A value from the System.Windows.Automation.Peers.PatternInterface enumeration. /// The object that supports the specified pattern, or null if unsupported. public override object GetPattern(PatternInterface patternInterface) { object result = null; switch (patternInterface) { case PatternInterface.SelectionItem: case PatternInterface.GridItem: { if (this.OwningCalendar != null && this.OwningCalendar.MonthControl != null && this.OwningCalendarButton != null) { result = this; } else { result = base.GetPattern(patternInterface); } break; } default: { result = base.GetPattern(patternInterface); break; } } return result; } #endregion Public methods #region Protected Methods /// /// Gets the control type for the element that is associated with the UI Automation peer. /// /// The control type. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Button; } /// /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, /// differentiates the control represented by this AutomationPeer. /// /// The string that contains the name. protected override string GetClassNameCore() { return Owner.GetType().Name; } /// /// Overrides the GetLocalizedControlTypeCore method for CalendarButtonAutomationPeer /// /// protected override string GetLocalizedControlTypeCore() { return SR.Get(SRID.CalendarAutomationPeer_CalendarButtonLocalizedControlType); } /// /// Overrides the GetHelpTextCore method for CalendarButtonAutomationPeer /// /// protected override string GetHelpTextCore() { DateTime? date = this.Date; return date.HasValue ? DateTimeHelper.ToLongDateString(date, DateTimeHelper.GetCulture(this.OwningCalendarButton)) : base.GetHelpTextCore(); } /// /// Overrides the GetNameCore method for CalendarButtonAutomationPeer /// /// protected override string GetNameCore() { DateTime? date = this.Date; if (date.HasValue) { if (this.OwningCalendar.DisplayMode == CalendarMode.Decade) { return DateTimeHelper.ToYearString(date, DateTimeHelper.GetCulture(this.OwningCalendarButton)); } else { return DateTimeHelper.ToYearMonthPatternString(date, DateTimeHelper.GetCulture(this.OwningCalendarButton)); } } else { return base.GetNameCore(); } } #endregion Protected Methods #region IGridItemProvider int IGridItemProvider.Column { get { return (int)this.OwningCalendarButton.GetValue(Grid.ColumnProperty); } } int IGridItemProvider.ColumnSpan { get { return (int)this.OwningCalendarButton.GetValue(Grid.ColumnSpanProperty); } } IRawElementProviderSimple IGridItemProvider.ContainingGrid { get { return this.OwningCalendarAutomationPeer; } } int IGridItemProvider.Row { get { return (int)this.OwningCalendarButton.GetValue(Grid.RowSpanProperty); } } int IGridItemProvider.RowSpan { get { return 1; } } #endregion IGridItemProvider #region ISelectionItemProvider bool ISelectionItemProvider.IsSelected { get { return this.OwningCalendarButton.IsFocused; } } IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { return this.OwningCalendarAutomationPeer; } } void ISelectionItemProvider.AddToSelection() { return; } void ISelectionItemProvider.RemoveFromSelection() { return; } void ISelectionItemProvider.Select() { if (this.OwningCalendarButton.IsEnabled) { this.OwningCalendarButton.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } else { throw new ElementNotEnabledException(); } } #endregion ISelectionItemProvider } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Automation/Peers/CalendarDayButtonAutomationPeer.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls; using Microsoft.Windows.Controls; using Microsoft.Windows.Controls.Primitives; namespace Microsoft.Windows.Automation.Peers { /// /// AutomationPeer for CalendarDayButton /// public sealed class CalendarDayButtonAutomationPeer : ButtonAutomationPeer, IGridItemProvider, ISelectionItemProvider, ITableItemProvider { /// /// Initializes a new instance of the CalendarDayButtonAutomationPeer class. /// /// Owning CalendarDayButton public CalendarDayButtonAutomationPeer(CalendarDayButton owner) : base(owner) { } #region Private Properties private Microsoft.Windows.Controls.Calendar OwningCalendar { get { return this.OwningCalendarDayButton.Owner; } } private IRawElementProviderSimple OwningCalendarAutomationPeer { get { if (this.OwningCalendar != null) { AutomationPeer peer = CreatePeerForElement(this.OwningCalendar); if (peer != null) { return ProviderFromPeer(peer); } } return null; } } private CalendarDayButton OwningCalendarDayButton { get { return this.Owner as CalendarDayButton; } } private DateTime? Date { get { if (this.OwningCalendarDayButton != null && this.OwningCalendarDayButton.DataContext is DateTime) { return (DateTime?)this.OwningCalendarDayButton.DataContext; } else { return null; } } } #endregion Private Properties #region Public Methods /// /// Gets the control pattern that is associated with the specified System.Windows.Automation.Peers.PatternInterface. /// /// A value from the System.Windows.Automation.Peers.PatternInterface enumeration. /// The object that supports the specified pattern, or null if unsupported. public override object GetPattern(PatternInterface patternInterface) { object result = null; switch (patternInterface) { case PatternInterface.SelectionItem: case PatternInterface.GridItem: case PatternInterface.TableItem: { if (this.OwningCalendar != null && this.OwningCalendarDayButton != null) { result = this; } else { result = base.GetPattern(patternInterface); } break; } default: { result = base.GetPattern(patternInterface); break; } } return result; } #endregion Public Methods #region Protected Methods /// /// Gets the control type for the element that is associated with the UI Automation peer. /// /// The control type. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Button; } /// /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, /// differentiates the control represented by this AutomationPeer. /// /// The string that contains the name. protected override string GetClassNameCore() { return Owner.GetType().Name; } /// /// Overrides the GetHelpTextCore method for CalendarDayButtonAutomationPeer /// /// protected override string GetHelpTextCore() { DateTime? date = this.Date; if (date.HasValue) { string dateString = DateTimeHelper.ToLongDateString(Date, DateTimeHelper.GetCulture(this.OwningCalendarDayButton)); if (this.OwningCalendarDayButton.IsBlackedOut) { return string.Format(DateTimeHelper.GetCurrentDateFormat(), SR.Get(SRID.CalendarAutomationPeer_BlackoutDayHelpText), dateString); } else { return dateString; } } else { return base.GetHelpTextCore(); } } /// /// Overrides the GetLocalizedControlTypeCore method for CalendarDayButtonAutomationPeer /// /// protected override string GetLocalizedControlTypeCore() { return SR.Get(SRID.CalendarAutomationPeer_DayButtonLocalizedControlType); } /// /// Overrides the GetNameCore method for CalendarDayButtonAutomationPeer /// /// protected override string GetNameCore() { DateTime? date = this.Date; return date.HasValue ? DateTimeHelper.ToLongDateString(Date, DateTimeHelper.GetCulture(this.OwningCalendarDayButton)) : base.GetNameCore(); } #endregion Protected Methods #region IGridItemProvider /// /// Grid item column. /// int IGridItemProvider.Column { get { return (int)this.OwningCalendarDayButton.GetValue(Grid.ColumnProperty); } } /// /// Grid item column span. /// int IGridItemProvider.ColumnSpan { get { return (int)this.OwningCalendarDayButton.GetValue(Grid.ColumnSpanProperty); } } /// /// Grid item's containing grid. /// IRawElementProviderSimple IGridItemProvider.ContainingGrid { get { return this.OwningCalendarAutomationPeer; } } /// /// Grid item row. /// int IGridItemProvider.Row { get { Debug.Assert((int)this.OwningCalendarDayButton.GetValue(Grid.RowProperty) > 0); // we decrement the Row value by one since the first row is composed of DayTitles return (int)this.OwningCalendarDayButton.GetValue(Grid.RowProperty) - 1; } } /// /// Grid item row span. /// int IGridItemProvider.RowSpan { get { return (int)this.OwningCalendarDayButton.GetValue(Grid.RowSpanProperty); } } #endregion IGridItemProvider #region ISelectionItemProvider /// /// True if the owning CalendarDayButton is selected. /// bool ISelectionItemProvider.IsSelected { get { return this.OwningCalendarDayButton.IsSelected; } } /// /// Selection items selection container. /// IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { return this.OwningCalendarAutomationPeer; } } /// /// Adds selection item to selection. /// void ISelectionItemProvider.AddToSelection() { // Return if the item is already selected or a day is already selected in the SingleSelectionMode if (((ISelectionItemProvider)this).IsSelected) { return; } if (EnsureSelection() && this.OwningCalendarDayButton.DataContext is DateTime) { if (this.OwningCalendar.SelectionMode == CalendarSelectionMode.SingleDate) { this.OwningCalendar.SelectedDate = (DateTime)this.OwningCalendarDayButton.DataContext; } else { this.OwningCalendar.SelectedDates.Add((DateTime)this.OwningCalendarDayButton.DataContext); } } } /// /// Removes selection item from selection. /// void ISelectionItemProvider.RemoveFromSelection() { // Return if the item is not already selected. if (!((ISelectionItemProvider)this).IsSelected) { return; } if (this.OwningCalendarDayButton.DataContext is DateTime) { this.OwningCalendar.SelectedDates.Remove((DateTime)this.OwningCalendarDayButton.DataContext); } } /// /// Selects this item. /// void ISelectionItemProvider.Select() { if (EnsureSelection()) { this.OwningCalendar.SelectedDates.Clear(); if (this.OwningCalendarDayButton.DataContext is DateTime) { this.OwningCalendar.SelectedDates.Add((DateTime)this.OwningCalendarDayButton.DataContext); } } } #endregion ISelectionItemProvider #region ITableItemProvider /// /// Gets the table item's column headers. /// /// The table item's column headers IRawElementProviderSimple[] ITableItemProvider.GetColumnHeaderItems() { if (this.OwningCalendar != null && this.OwningCalendarAutomationPeer != null) { IRawElementProviderSimple[] headers = ((ITableProvider)CreatePeerForElement(this.OwningCalendar)).GetColumnHeaders(); if (headers != null) { int column = ((IGridItemProvider)this).Column; return new IRawElementProviderSimple[] { headers[column] }; } } return null; } /// /// Get's the table item's row headers. /// /// The table item's row headers IRawElementProviderSimple[] ITableItemProvider.GetRowHeaderItems() { return null; } #endregion ITableItemProvider #region Private Methods private bool EnsureSelection() { if (!this.OwningCalendarDayButton.IsEnabled) { throw new ElementNotEnabledException(); } // If the day is a blackout day or the SelectionMode is None, selection is not allowed if (this.OwningCalendarDayButton.IsBlackedOut || this.OwningCalendar.SelectionMode == CalendarSelectionMode.None) { return false; } return true; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/Calendar.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Input; using Microsoft.Windows.Automation.Peers; using Microsoft.Windows.Controls.Primitives; namespace Microsoft.Windows.Controls { /// /// Represents a control that enables a user to select a date by using a visual calendar display. /// [TemplatePart(Name = Calendar.ElementRoot, Type = typeof(Panel))] [TemplatePart(Name = Calendar.ElementMonth, Type = typeof(CalendarItem))] public class Calendar : Control { #region Constants private const string ElementRoot = "PART_Root"; private const string ElementMonth = "PART_CalendarItem"; private const int COLS = 7; private const int ROWS = 7; private const int YEAR_ROWS = 3; private const int YEAR_COLS = 4; private const int YEARS_PER_DECADE = 10; #endregion Constants #region Data private DateTime? _hoverStart; private DateTime? _hoverEnd; private bool _isShiftPressed; private DateTime? _currentDate; private CalendarItem _monthControl; private CalendarBlackoutDatesCollection _blackoutDates; private SelectedDatesCollection _selectedDates; #endregion Data #region Public Events public static readonly RoutedEvent SelectedDatesChangedEvent = EventManager.RegisterRoutedEvent("SelectedDatesChanged", RoutingStrategy.Direct, typeof(EventHandler), typeof(Calendar)); /// /// Occurs when a date is selected. /// public event EventHandler SelectedDatesChanged { add { AddHandler(SelectedDatesChangedEvent, value); } remove { RemoveHandler(SelectedDatesChangedEvent, value); } } /// /// Occurs when the DisplayDate property is changed. /// public event EventHandler DisplayDateChanged; /// /// Occurs when the DisplayMode property is changed. /// public event EventHandler DisplayModeChanged; /// /// Occurs when the SelectionMode property is changed. /// public event EventHandler SelectionModeChanged; #endregion Public Events /// /// Static constructor /// static Calendar() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(typeof(Calendar))); KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained)); EventManager.RegisterClassHandler(typeof(Calendar), UIElement.GotFocusEvent, new RoutedEventHandler(OnGotFocus)); LanguageProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnLanguageChanged))); } /// /// Initializes a new instance of the Calendar class. /// public Calendar() { this._blackoutDates = new CalendarBlackoutDatesCollection(this); this._selectedDates = new SelectedDatesCollection(this); this.DisplayDate = DateTime.Today; } #region Public Properties #region BlackoutDates /// /// Gets or sets the dates that are not selectable. /// public CalendarBlackoutDatesCollection BlackoutDates { get { return _blackoutDates; } } #endregion BlackoutDates #region CalendarButtonStyle /// /// Gets or sets the style for displaying a CalendarButton. /// public Style CalendarButtonStyle { get { return (Style)GetValue(CalendarButtonStyleProperty); } set { SetValue(CalendarButtonStyleProperty, value); } } /// /// Identifies the CalendarButtonStyle dependency property. /// public static readonly DependencyProperty CalendarButtonStyleProperty = DependencyProperty.Register( "CalendarButtonStyle", typeof(Style), typeof(Calendar)); #endregion CalendarButtonStyle #region CalendarDayButtonStyle /// /// Gets or sets the style for displaying a day. /// public Style CalendarDayButtonStyle { get { return (Style)GetValue(CalendarDayButtonStyleProperty); } set { SetValue(CalendarDayButtonStyleProperty, value); } } /// /// Identifies the DayButtonStyle dependency property. /// public static readonly DependencyProperty CalendarDayButtonStyleProperty = DependencyProperty.Register( "CalendarDayButtonStyle", typeof(Style), typeof(Calendar)); #endregion CalendarDayButtonStyle #region CalendarItemStyle /// /// Gets or sets the style for a Month. /// public Style CalendarItemStyle { get { return (Style)GetValue(CalendarItemStyleProperty); } set { SetValue(CalendarItemStyleProperty, value); } } /// /// Identifies the MonthStyle dependency property. /// public static readonly DependencyProperty CalendarItemStyleProperty = DependencyProperty.Register( "CalendarItemStyle", typeof(Style), typeof(Calendar)); #endregion CalendarItemStyle #region DisplayDate /// /// Gets or sets the date to display. /// /// public DateTime DisplayDate { get { return (DateTime)GetValue(DisplayDateProperty); } set { SetValue(DisplayDateProperty, value); } } /// /// Identifies the DisplayDate dependency property. /// public static readonly DependencyProperty DisplayDateProperty = DependencyProperty.Register( "DisplayDate", typeof(DateTime), typeof(Calendar), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateChanged, CoerceDisplayDate)); /// /// DisplayDateProperty property changed handler. /// /// Calendar that changed its DisplayDate. /// DependencyPropertyChangedEventArgs. private static void OnDisplayDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; Debug.Assert(c != null); c.DisplayDateInternal = DateTimeHelper.DiscardDayTime((DateTime)e.NewValue); c.UpdateCellItems(); c.OnDisplayDateChanged(new CalendarDateChangedEventArgs((DateTime)e.OldValue, (DateTime)e.NewValue)); } private static object CoerceDisplayDate(DependencyObject d, object value) { Calendar c = d as Calendar; DateTime date = (DateTime)value; if (c.DisplayDateStart.HasValue && (date < c.DisplayDateStart.Value)) { value = c.DisplayDateStart.Value; } else if (c.DisplayDateEnd.HasValue && (date > c.DisplayDateEnd.Value)) { value = c.DisplayDateEnd.Value; } return value; } #endregion DisplayDate #region DisplayDateEnd /// /// Gets or sets the last date to be displayed. /// /// public DateTime? DisplayDateEnd { get { return (DateTime?)GetValue(DisplayDateEndProperty); } set { SetValue(DisplayDateEndProperty, value); } } /// /// Identifies the DisplayDateEnd dependency property. /// public static readonly DependencyProperty DisplayDateEndProperty = DependencyProperty.Register( "DisplayDateEnd", typeof(DateTime?), typeof(Calendar), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateEndChanged, CoerceDisplayDateEnd)); /// /// DisplayDateEndProperty property changed handler. /// /// Calendar that changed its DisplayDateEnd. /// DependencyPropertyChangedEventArgs. private static void OnDisplayDateEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; Debug.Assert(c != null); c.CoerceValue(DisplayDateProperty); c.UpdateCellItems(); } private static object CoerceDisplayDateEnd(DependencyObject d, object value) { Calendar c = d as Calendar; DateTime? date = (DateTime?)value; if (date.HasValue) { if (c.DisplayDateStart.HasValue && (date.Value < c.DisplayDateStart.Value)) { value = c.DisplayDateStart; } DateTime? maxSelectedDate = c.SelectedDates.MaximumDate; if (maxSelectedDate.HasValue && (date.Value < maxSelectedDate.Value)) { value = maxSelectedDate; } } return value; } #endregion DisplayDateEnd #region DisplayDateStart /// /// Gets or sets the first date to be displayed. /// /// public DateTime? DisplayDateStart { get { return (DateTime?)GetValue(DisplayDateStartProperty); } set { SetValue(DisplayDateStartProperty, value); } } /// /// Identifies the DisplayDateStart dependency property. /// public static readonly DependencyProperty DisplayDateStartProperty = DependencyProperty.Register( "DisplayDateStart", typeof(DateTime?), typeof(Calendar), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateStartChanged, CoerceDisplayDateStart)); /// /// DisplayDateStartProperty property changed handler. /// /// Calendar that changed its DisplayDateStart. /// DependencyPropertyChangedEventArgs. private static void OnDisplayDateStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; Debug.Assert(c != null); c.CoerceValue(DisplayDateEndProperty); c.CoerceValue(DisplayDateProperty); c.UpdateCellItems(); } private static object CoerceDisplayDateStart(DependencyObject d, object value) { Calendar c = d as Calendar; DateTime? date = (DateTime?)value; if (date.HasValue) { DateTime? minSelectedDate = c.SelectedDates.MinimumDate; if (minSelectedDate.HasValue && (date.Value > minSelectedDate.Value)) { value = minSelectedDate; } } return value; } #endregion DisplayDateStart #region DisplayMode /// /// Gets or sets a value indicating whether the calendar is displayed in months or years. /// public CalendarMode DisplayMode { get { return (CalendarMode)GetValue(DisplayModeProperty); } set { SetValue(DisplayModeProperty, value); } } /// /// Identifies the DisplayMode dependency property. /// public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register( "DisplayMode", typeof(CalendarMode), typeof(Calendar), new FrameworkPropertyMetadata(CalendarMode.Month, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayModePropertyChanged), new ValidateValueCallback(IsValidDisplayMode)); /// /// DisplayModeProperty property changed handler. /// /// Calendar that changed its DisplayMode. /// DependencyPropertyChangedEventArgs. private static void OnDisplayModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; Debug.Assert(c != null); CalendarMode mode = (CalendarMode)e.NewValue; CalendarMode oldMode = (CalendarMode)e.OldValue; CalendarItem monthControl = c.MonthControl; switch (mode) { case CalendarMode.Month: { if (oldMode == CalendarMode.Year || oldMode == CalendarMode.Decade) { // Cancel highlight when switching to month display mode c.HoverStart = c.HoverEnd = null; c.CurrentDate = c.DisplayDate; } c.UpdateCellItems(); break; } case CalendarMode.Year: case CalendarMode.Decade: { if (oldMode == CalendarMode.Month) { c.DisplayDate = c.CurrentDate; } c.UpdateCellItems(); break; } default: Debug.Assert(false); break; } c.OnDisplayModeChanged(new CalendarModeChangedEventArgs((CalendarMode)e.OldValue, mode)); } #endregion DisplayMode #region FirstDayOfWeek /// /// Gets or sets the day that is considered the beginning of the week. /// public DayOfWeek FirstDayOfWeek { get { return (DayOfWeek)GetValue(FirstDayOfWeekProperty); } set { SetValue(FirstDayOfWeekProperty, value); } } /// /// Identifies the FirstDayOfWeek dependency property. /// public static readonly DependencyProperty FirstDayOfWeekProperty = DependencyProperty.Register( "FirstDayOfWeek", typeof(DayOfWeek), typeof(Calendar), new FrameworkPropertyMetadata(DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek, OnFirstDayOfWeekChanged), new ValidateValueCallback(IsValidFirstDayOfWeek)); /// /// FirstDayOfWeekProperty property changed handler. /// /// Calendar that changed its FirstDayOfWeek. /// DependencyPropertyChangedEventArgs. private static void OnFirstDayOfWeekChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; c.UpdateCellItems(); } #endregion FirstDayOfWeek #region IsTodayHighlighted /// /// Gets or sets a value indicating whether the current date is highlighted. /// public bool IsTodayHighlighted { get { return (bool)GetValue(IsTodayHighlightedProperty); } set { SetValue(IsTodayHighlightedProperty, value); } } /// /// Identifies the IsTodayHighlighted dependency property. /// public static readonly DependencyProperty IsTodayHighlightedProperty = DependencyProperty.Register( "IsTodayHighlighted", typeof(bool), typeof(Calendar), new FrameworkPropertyMetadata(true, OnIsTodayHighlightedChanged)); /// /// IsTodayHighlightedProperty property changed handler. /// /// Calendar that changed its IsTodayHighlighted. /// DependencyPropertyChangedEventArgs. private static void OnIsTodayHighlightedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; int i = DateTimeHelper.CompareYearMonth(c.DisplayDateInternal, DateTime.Today); if (i > -2 && i < 2) { c.UpdateCellItems(); } } #endregion IsTodayHighlighted #region Language private static void OnLanguageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; if (DependencyPropertyHelper.GetValueSource(d, Calendar.FirstDayOfWeekProperty).BaseValueSource == BaseValueSource.Default) { c.CoerceValue(FirstDayOfWeekProperty); c.UpdateCellItems(); } } #endregion #region SelectedDate /// /// Gets or sets the currently selected date. /// /// public DateTime? SelectedDate { get { return (DateTime?)GetValue(SelectedDateProperty); } set { SetValue(SelectedDateProperty, value); } } /// /// Identifies the SelectedDate dependency property. /// public static readonly DependencyProperty SelectedDateProperty = DependencyProperty.Register( "SelectedDate", typeof(DateTime?), typeof(Calendar), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateChanged)); /// /// SelectedDateProperty property changed handler. /// /// Calendar that changed its SelectedDate. /// DependencyPropertyChangedEventArgs. private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; Debug.Assert(c != null); if (c.SelectionMode != CalendarSelectionMode.None || e.NewValue == null) { DateTime? addedDate; addedDate = (DateTime?)e.NewValue; if (IsValidDateSelection(c, addedDate)) { if (!addedDate.HasValue) { c.SelectedDates.ClearInternal(true /*fireChangeNotification*/); } else { if (addedDate.HasValue && !(c.SelectedDates.Count > 0 && c.SelectedDates[0] == addedDate.Value)) { c.SelectedDates.ClearInternal(); c.SelectedDates.Add(addedDate.Value); } } // We update the current date for only the Single mode.For the other modes it automatically gets updated if (c.SelectionMode == CalendarSelectionMode.SingleDate) { if (addedDate.HasValue) { c.CurrentDate = addedDate.Value; } c.UpdateCellItems(); } } else { throw new ArgumentOutOfRangeException("d", SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue)); } } else { throw new InvalidOperationException(SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidOperation)); } } #endregion SelectedDate #region SelectedDates // TODO: Should it be of type ObservableCollection? /// /// Gets the dates that are currently selected. /// public SelectedDatesCollection SelectedDates { get { return _selectedDates; } } #endregion SelectedDates #region SelectionMode /// /// Gets or sets the selection mode for the calendar. /// public CalendarSelectionMode SelectionMode { get { return (CalendarSelectionMode)GetValue(SelectionModeProperty); } set { SetValue(SelectionModeProperty, value); } } /// /// Identifies the SelectionMode dependency property. /// public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register( "SelectionMode", typeof(CalendarSelectionMode), typeof(Calendar), new FrameworkPropertyMetadata(CalendarSelectionMode.SingleDate, OnSelectionModeChanged), new ValidateValueCallback(IsValidSelectionMode)); private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Calendar c = d as Calendar; Debug.Assert(c != null); c.HoverStart = c.HoverEnd = null; c.SelectedDates.ClearInternal(true /*fireChangeNotification*/); c.OnSelectionModeChanged(EventArgs.Empty); } #endregion SelectionMode #endregion Public Properties #region Internal Events internal event MouseButtonEventHandler DayButtonMouseUp; internal event RoutedEventHandler DayOrMonthPreviewKeyDown; #endregion Internal Events #region Internal Properties /// /// This flag is used to determine whether DatePicker should change its /// DisplayDate because of a SelectedDate change on its Calendar /// internal bool DatePickerDisplayDateFlag { get; set; } internal DateTime DisplayDateInternal { get; private set; } internal DateTime DisplayDateEndInternal { get { return this.DisplayDateEnd.GetValueOrDefault(DateTime.MaxValue); } } internal DateTime DisplayDateStartInternal { get { return this.DisplayDateStart.GetValueOrDefault(DateTime.MinValue); } } internal DateTime CurrentDate { get { return _currentDate.GetValueOrDefault(this.DisplayDateInternal); } set { _currentDate = value; } } internal DateTime? HoverStart { get { return this.SelectionMode == CalendarSelectionMode.None ? null : _hoverStart; } set { _hoverStart = value; } } internal DateTime? HoverEnd { get { return this.SelectionMode == CalendarSelectionMode.None ? null : _hoverEnd; } set { _hoverEnd = value; } } internal CalendarItem MonthControl { get { return _monthControl; } } internal DateTime DisplayMonth { get { return DateTimeHelper.DiscardDayTime(DisplayDate); } } internal DateTime DisplayYear { get { return new DateTime(DisplayDate.Year, 1, 1); } } #endregion Internal Properties #region Private Properties private bool IsRightToLeft { get { return FlowDirection == FlowDirection.RightToLeft; } } #endregion Private Properties #region Public Methods /// /// Invoked whenever application code or an internal process, /// such as a rebuilding layout pass, calls the ApplyTemplate method. /// public override void OnApplyTemplate() { if (_monthControl != null) { _monthControl.Owner = null; } base.OnApplyTemplate(); _monthControl = GetTemplateChild(ElementMonth) as CalendarItem; if (_monthControl != null) { _monthControl.Owner = this; } this.CurrentDate = this.DisplayDate; UpdateCellItems(); } /// /// Provides a text representation of the selected date. /// /// A text representation of the selected date, or an empty string if SelectedDate is a null reference. public override string ToString() { if (this.SelectedDate != null) { return this.SelectedDate.Value.ToString(DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this))); } else { return string.Empty; } } #endregion Public Methods #region Protected Methods protected virtual void OnSelectedDatesChanged(SelectionChangedEventArgs e) { RaiseEvent(e); } protected virtual void OnDisplayDateChanged(CalendarDateChangedEventArgs e) { EventHandler handler = this.DisplayDateChanged; if (handler != null) { handler(this, e); } } protected virtual void OnDisplayModeChanged(CalendarModeChangedEventArgs e) { EventHandler handler = this.DisplayModeChanged; if (handler != null) { handler(this, e); } } protected virtual void OnSelectionModeChanged(EventArgs e) { EventHandler handler = this.SelectionModeChanged; if (handler != null) { handler(this, e); } } /// /// Creates the automation peer for this Calendar Control. /// /// protected override AutomationPeer OnCreateAutomationPeer() { return new CalendarAutomationPeer(this); } protected override void OnKeyDown(KeyEventArgs e) { if (!e.Handled) { e.Handled = ProcessCalendarKey(e); } } protected override void OnKeyUp(KeyEventArgs e) { if (!e.Handled) { if (e.Key == Key.LeftShift || e.Key == Key.RightShift) { ProcessShiftKeyUp(); } } } #endregion Protected Methods #region Internal Methods internal CalendarDayButton FindDayButtonFromDay(DateTime day) { if (this.MonthControl != null) { foreach (CalendarDayButton b in this.MonthControl.GetCalendarDayButtons()) { if (b.DataContext is DateTime) { if (DateTimeHelper.CompareDays((DateTime)b.DataContext, day) == 0) { return b; } } } } return null; } internal static bool IsValidDateSelection(Calendar cal, object value) { return (value == null) || (!cal.BlackoutDates.Contains((DateTime)value)); } internal void OnDayButtonMouseUp(MouseButtonEventArgs e) { MouseButtonEventHandler handler = this.DayButtonMouseUp; if (null != handler) { handler(this, e); } } internal void OnDayOrMonthPreviewKeyDown(RoutedEventArgs e) { RoutedEventHandler handler = this.DayOrMonthPreviewKeyDown; if (null != handler) { handler(this, e); } } // If the day is a trailing day, Update the DisplayDate internal void OnDayClick(DateTime selectedDate) { if (this.SelectionMode == CalendarSelectionMode.None) { this.CurrentDate = selectedDate; } if (DateTimeHelper.CompareYearMonth(selectedDate, this.DisplayDateInternal) != 0) { MoveDisplayTo(selectedDate); } else { UpdateCellItems(); FocusDate(selectedDate); } } internal void OnCalendarButtonPressed(CalendarButton b, bool switchDisplayMode) { if (b.DataContext is DateTime) { DateTime d = (DateTime)b.DataContext; DateTime? newDate = null; CalendarMode newMode = CalendarMode.Month; switch (this.DisplayMode) { case CalendarMode.Month: { Debug.Assert(false); break; } case CalendarMode.Year: { newDate = DateTimeHelper.SetYearMonth(this.DisplayDate, d); newMode = CalendarMode.Month; break; } case CalendarMode.Decade: { newDate = DateTimeHelper.SetYear(this.DisplayDate, d.Year); newMode = CalendarMode.Year; break; } default: Debug.Assert(false); break; } if (newDate.HasValue) { this.DisplayDate = newDate.Value; if (switchDisplayMode) { this.DisplayMode = newMode; FocusDate(this.DisplayMode == CalendarMode.Month ? this.CurrentDate : this.DisplayDate); } } } } private DateTime? GetDateOffset(DateTime date, int offset, CalendarMode displayMode) { DateTime? result = null; switch (displayMode) { case CalendarMode.Month: { result = DateTimeHelper.AddMonths(date, offset); break; } case CalendarMode.Year: { result = DateTimeHelper.AddYears(date, offset); break; } case CalendarMode.Decade: { result = DateTimeHelper.AddYears(this.DisplayDate, offset * YEARS_PER_DECADE); break; } default: Debug.Assert(false); break; } return result; } private void MoveDisplayTo(DateTime? date) { if (date.HasValue) { DateTime d = date.Value.Date; switch (this.DisplayMode) { case CalendarMode.Month: { this.DisplayDate = DateTimeHelper.DiscardDayTime(d); this.CurrentDate = d; UpdateCellItems(); break; } case CalendarMode.Year: case CalendarMode.Decade: { this.DisplayDate = d; UpdateCellItems(); break; } default: Debug.Assert(false); break; } FocusDate(d); } } internal void OnNextClick() { DateTime? nextDate = GetDateOffset(this.DisplayDate, 1, this.DisplayMode); if (nextDate.HasValue) { MoveDisplayTo(DateTimeHelper.DiscardDayTime(nextDate.Value)); } } internal void OnPreviousClick() { DateTime? nextDate = GetDateOffset(this.DisplayDate, -1, this.DisplayMode); if (nextDate.HasValue) { MoveDisplayTo(DateTimeHelper.DiscardDayTime(nextDate.Value)); } } internal void OnSelectedDatesCollectionChanged(SelectionChangedEventArgs e) { if (IsSelectionChanged(e)) { if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) || AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) || AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) { CalendarAutomationPeer peer = FrameworkElementAutomationPeer.FromElement(this) as CalendarAutomationPeer; if (peer != null) { peer.RaiseSelectionEvents(e); } } CoerceFromSelection(); OnSelectedDatesChanged(e); } } internal void UpdateCellItems() { CalendarItem monthControl = this.MonthControl; if (monthControl != null) { switch (this.DisplayMode) { case CalendarMode.Month: { monthControl.UpdateMonthMode(); break; } case CalendarMode.Year: { monthControl.UpdateYearMode(); break; } case CalendarMode.Decade: { monthControl.UpdateDecadeMode(); break; } default: Debug.Assert(false); break; } } } #endregion Internal Methods #region Private Methods private void CoerceFromSelection() { CoerceValue(DisplayDateStartProperty); CoerceValue(DisplayDateEndProperty); CoerceValue(DisplayDateProperty); } // This method adds the days that were selected by Keyboard to the SelectedDays Collection private void AddKeyboardSelection() { if (this.HoverStart != null) { this.SelectedDates.ClearInternal(); // In keyboard selection, we are sure that the collection does not include any blackout days this.SelectedDates.AddRange(this.HoverStart.Value, this.CurrentDate); } } private static bool IsSelectionChanged(SelectionChangedEventArgs e) { if (e.AddedItems.Count != e.RemovedItems.Count) { return true; } foreach (DateTime addedDate in e.AddedItems) { if (!e.RemovedItems.Contains(addedDate)) { return true; } } return false; } private static bool IsValidDisplayMode(object value) { CalendarMode mode = (CalendarMode)value; return mode == CalendarMode.Month || mode == CalendarMode.Year || mode == CalendarMode.Decade; } internal static bool IsValidFirstDayOfWeek(object value) { DayOfWeek day = (DayOfWeek)value; return day == DayOfWeek.Sunday || day == DayOfWeek.Monday || day == DayOfWeek.Tuesday || day == DayOfWeek.Wednesday || day == DayOfWeek.Thursday || day == DayOfWeek.Friday || day == DayOfWeek.Saturday; } private static bool IsValidKeyboardSelection(Calendar cal, object value) { if (value == null) { return true; } else { if (cal.BlackoutDates.Contains((DateTime)value)) { return false; } else { return DateTime.Compare((DateTime)value, cal.DisplayDateStartInternal) >= 0 && DateTime.Compare((DateTime)value, cal.DisplayDateEndInternal) <= 0; } } } private static bool IsValidSelectionMode(object value) { CalendarSelectionMode mode = (CalendarSelectionMode)value; return mode == CalendarSelectionMode.SingleDate || mode == CalendarSelectionMode.SingleRange || mode == CalendarSelectionMode.MultipleRange || mode == CalendarSelectionMode.None; } private void OnSelectedMonthChanged(DateTime? selectedMonth) { if (selectedMonth.HasValue) { Debug.Assert(this.DisplayMode == CalendarMode.Year); this.DisplayDate = selectedMonth.Value; UpdateCellItems(); FocusDate(selectedMonth.Value); } } private void OnSelectedYearChanged(DateTime? selectedYear) { if (selectedYear.HasValue) { Debug.Assert(this.DisplayMode == CalendarMode.Decade); this.DisplayDate = selectedYear.Value; UpdateCellItems(); FocusDate(selectedYear.Value); } } internal void FocusDate(DateTime date) { if (MonthControl != null) { MonthControl.FocusDate(date); } } /// /// Called when this element gets focus. /// private static void OnGotFocus(object sender, RoutedEventArgs e) { // When Calendar gets focus move it to the DisplayDate var c = (Calendar)sender; if (!e.Handled && e.OriginalSource == c) { // This check is for the case where the DisplayDate is the first of the month // and the SelectedDate is in the middle of the month. If you tab into the Calendar // the focus should go to the SelectedDate, not the DisplayDate. if (c.SelectedDate.HasValue && DateTimeHelper.CompareYearMonth(c.SelectedDate.Value, c.DisplayDateInternal) == 0) { c.FocusDate(c.SelectedDate.Value); } else { c.FocusDate(c.DisplayDate); } e.Handled = true; } } private bool ProcessCalendarKey(KeyEventArgs e) { if (this.DisplayMode == CalendarMode.Month) { // If a blackout day is inactive, when clicked on it, the previous inactive day which is not a blackout day can get the focus. // In this case we should allow keyboard functions on that inactive day CalendarDayButton currentDayButton = (MonthControl != null) ? MonthControl.GetCalendarDayButton(this.CurrentDate) : null; if (DateTimeHelper.CompareYearMonth(this.CurrentDate, this.DisplayDateInternal) != 0 && currentDayButton != null && !currentDayButton.IsInactive) { return false; } } bool ctrl, shift; KeyboardHelper.GetMetaKeyState(out ctrl, out shift); switch (e.Key) { case Key.Up: { ProcessUpKey(ctrl, shift); return true; } case Key.Down: { ProcessDownKey(ctrl, shift); return true; } case Key.Left: { ProcessLeftKey(shift); return true; } case Key.Right: { ProcessRightKey(shift); return true; } case Key.PageDown: { ProcessPageDownKey(shift); return true; } case Key.PageUp: { ProcessPageUpKey(shift); return true; } case Key.Home: { ProcessHomeKey(shift); return true; } case Key.End: { ProcessEndKey(shift); return true; } case Key.Enter: case Key.Space: { return ProcessEnterKey(); } } return false; } private void ProcessDownKey(bool ctrl, bool shift) { switch (this.DisplayMode) { case CalendarMode.Month: { if (!ctrl || shift) { DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, COLS), 1); ProcessSelection(shift, selectedDate); } break; } case CalendarMode.Year: { if (ctrl) { this.DisplayMode = CalendarMode.Month; FocusDate(this.DisplayDate); } else { DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, YEAR_COLS); OnSelectedMonthChanged(selectedMonth); } break; } case CalendarMode.Decade: { if (ctrl) { this.DisplayMode = CalendarMode.Year; FocusDate(this.DisplayDate); } else { DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, YEAR_COLS); OnSelectedYearChanged(selectedYear); } break; } } } private void ProcessEndKey(bool shift) { switch (this.DisplayMode) { case CalendarMode.Month: { if (this.DisplayDate != null) { DateTime? selectedDate = new DateTime(this.DisplayDateInternal.Year, this.DisplayDateInternal.Month, 1); if (DateTimeHelper.CompareYearMonth(DateTime.MaxValue, selectedDate.Value) > 0) { // since DisplayDate is not equal to DateTime.MaxValue we are sure selectedDate is not null selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1).Value; selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1).Value; } else { selectedDate = DateTime.MaxValue; } ProcessSelection(shift, selectedDate); } break; } case CalendarMode.Year: { DateTime selectedMonth = new DateTime(this.DisplayDate.Year, 12, 1); OnSelectedMonthChanged(selectedMonth); break; } case CalendarMode.Decade: { DateTime? selectedYear = new DateTime(DateTimeHelper.EndOfDecade(this.DisplayDate), 1, 1); OnSelectedYearChanged(selectedYear); break; } } } private bool ProcessEnterKey() { switch (this.DisplayMode) { case CalendarMode.Year: { this.DisplayMode = CalendarMode.Month; FocusDate(this.DisplayDate); return true; } case CalendarMode.Decade: { this.DisplayMode = CalendarMode.Year; FocusDate(this.DisplayDate); return true; } } return false; } private void ProcessHomeKey(bool shift) { switch (this.DisplayMode) { case CalendarMode.Month: { // TODO: Not all types of calendars start with Day1. If Non-Gregorian is supported check this: DateTime? selectedDate = new DateTime(this.DisplayDateInternal.Year, this.DisplayDateInternal.Month, 1); ProcessSelection(shift, selectedDate); break; } case CalendarMode.Year: { DateTime selectedMonth = new DateTime(this.DisplayDate.Year, 1, 1); OnSelectedMonthChanged(selectedMonth); break; } case CalendarMode.Decade: { DateTime? selectedYear = new DateTime(DateTimeHelper.DecadeOfDate(this.DisplayDate), 1, 1); OnSelectedYearChanged(selectedYear); break; } } } private void ProcessLeftKey(bool shift) { int moveAmmount = (!this.IsRightToLeft) ? -1 : 1; switch (this.DisplayMode) { case CalendarMode.Month: { DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, moveAmmount), moveAmmount); ProcessSelection(shift, selectedDate); break; } case CalendarMode.Year: { DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, moveAmmount); OnSelectedMonthChanged(selectedMonth); break; } case CalendarMode.Decade: { DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, moveAmmount); OnSelectedYearChanged(selectedYear); break; } } } private void ProcessPageDownKey(bool shift) { switch (this.DisplayMode) { case CalendarMode.Month: { DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddMonths(this.CurrentDate, 1), 1); ProcessSelection(shift, selectedDate); break; } case CalendarMode.Year: { DateTime? selectedMonth = DateTimeHelper.AddYears(this.DisplayDate, 1); OnSelectedMonthChanged(selectedMonth); break; } case CalendarMode.Decade: { DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, 10); OnSelectedYearChanged(selectedYear); break; } } } private void ProcessPageUpKey(bool shift) { switch (this.DisplayMode) { case CalendarMode.Month: { DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddMonths(this.CurrentDate, -1), -1); ProcessSelection(shift, selectedDate); break; } case CalendarMode.Year: { DateTime? selectedMonth = DateTimeHelper.AddYears(this.DisplayDate, -1); OnSelectedMonthChanged(selectedMonth); break; } case CalendarMode.Decade: { DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, -10); OnSelectedYearChanged(selectedYear); break; } } } private void ProcessRightKey(bool shift) { int moveAmmount = (!this.IsRightToLeft) ? 1 : -1; switch (this.DisplayMode) { case CalendarMode.Month: { DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, moveAmmount), moveAmmount); ProcessSelection(shift, selectedDate); break; } case CalendarMode.Year: { DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, moveAmmount); OnSelectedMonthChanged(selectedMonth); break; } case CalendarMode.Decade: { DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, moveAmmount); OnSelectedYearChanged(selectedYear); break; } } } private void ProcessSelection(bool shift, DateTime? lastSelectedDate) { if (this.SelectionMode == CalendarSelectionMode.None && lastSelectedDate != null) { OnDayClick(lastSelectedDate.Value); return; } if (lastSelectedDate != null && IsValidKeyboardSelection(this, lastSelectedDate.Value)) { if (this.SelectionMode == CalendarSelectionMode.SingleRange || this.SelectionMode == CalendarSelectionMode.MultipleRange) { this.SelectedDates.ClearInternal(); if (shift) { this._isShiftPressed = true; if (!this.HoverStart.HasValue) { this.HoverStart = this.HoverEnd = this.CurrentDate; } // If we hit a BlackOutDay with keyboard we do not update the HoverEnd CalendarDateRange range; if (DateTime.Compare(this.HoverStart.Value, lastSelectedDate.Value) < 0) { range = new CalendarDateRange(this.HoverStart.Value, lastSelectedDate.Value); } else { range = new CalendarDateRange(lastSelectedDate.Value, this.HoverStart.Value); } if (!this.BlackoutDates.ContainsAny(range)) { this._currentDate = lastSelectedDate; this.HoverEnd = lastSelectedDate; } OnDayClick(this.CurrentDate); } else { this.HoverStart = this.HoverEnd = this.CurrentDate = lastSelectedDate.Value; AddKeyboardSelection(); OnDayClick(lastSelectedDate.Value); } } else { // ON CLEAR this.CurrentDate = lastSelectedDate.Value; this.HoverStart = this.HoverEnd = null; if (this.SelectedDates.Count > 0) { this.SelectedDates[0] = lastSelectedDate.Value; } else { this.SelectedDates.Add(lastSelectedDate.Value); } OnDayClick(lastSelectedDate.Value); } UpdateCellItems(); } } private void ProcessShiftKeyUp() { if (this._isShiftPressed && (this.SelectionMode == CalendarSelectionMode.SingleRange || this.SelectionMode == CalendarSelectionMode.MultipleRange)) { AddKeyboardSelection(); this._isShiftPressed = false; this.HoverStart = this.HoverEnd = null; } } private void ProcessUpKey(bool ctrl, bool shift) { switch (this.DisplayMode) { case CalendarMode.Month: { if (ctrl) { this.DisplayMode = CalendarMode.Year; FocusDate(this.DisplayDate); } else { DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, -COLS), -1); ProcessSelection(shift, selectedDate); } break; } case CalendarMode.Year: { if (ctrl) { this.DisplayMode = CalendarMode.Decade; FocusDate(this.DisplayDate); } else { DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, -YEAR_COLS); OnSelectedMonthChanged(selectedMonth); } break; } case CalendarMode.Decade: { if (!ctrl) { DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, -YEAR_COLS); OnSelectedYearChanged(selectedYear); } break; } } } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarBlackoutDatesCollection.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Threading; namespace Microsoft.Windows.Controls { /// /// Represents a collection of DateTimeRanges. /// public sealed class CalendarBlackoutDatesCollection : ObservableCollection { #region Data private Thread _dispatcherThread; private Calendar _owner; #endregion Data /// /// Initializes a new instance of the CalendarBlackoutDatesCollection class. /// /// public CalendarBlackoutDatesCollection(Calendar owner) { _owner = owner; this._dispatcherThread = Thread.CurrentThread; } #region Public Methods /// /// Dates that are in the past are added to the BlackoutDates. /// public void AddDatesInPast() { this.Add(new CalendarDateRange(DateTime.MinValue, DateTime.Today.AddDays(-1))); } /// /// Checks if a DateTime is in the Collection /// /// /// public bool Contains(DateTime date) { return null != GetContainingDateRange(date); } /// /// Checks if a Range is in the collection /// /// /// /// public bool Contains(DateTime start, DateTime end) { DateTime rangeStart, rangeEnd; int n = Count; if (DateTime.Compare(end, start) > -1) { rangeStart = DateTimeHelper.DiscardTime(start).Value; rangeEnd = DateTimeHelper.DiscardTime(end).Value; } else { rangeStart = DateTimeHelper.DiscardTime(end).Value; rangeEnd = DateTimeHelper.DiscardTime(start).Value; } for (int i = 0; i < n; i++) { if (DateTime.Compare(this[i].Start, rangeStart) == 0 && DateTime.Compare(this[i].End, rangeEnd) == 0) { return true; } } return false; } /// /// Returns true if any day in the given DateTime range is contained in the BlackOutDays. /// /// CalendarDateRange that is searched in BlackOutDays /// true if at least one day in the range is included in the BlackOutDays public bool ContainsAny(CalendarDateRange range) { foreach (CalendarDateRange item in this) { if (item.ContainsAny(range)) { return true; } } return false; } /// /// This finds the next date that is not blacked out in a certian direction. /// /// /// /// internal DateTime? GetNonBlackoutDate(DateTime? requestedDate, int dayInterval) { Debug.Assert(dayInterval != 0); DateTime? currentDate = requestedDate; CalendarDateRange range = null; if (requestedDate == null) { return null; } if ((range = GetContainingDateRange((DateTime)currentDate)) == null) { return requestedDate; } do { if (dayInterval > 0) { // Moving Forwards. // The DateRanges require start <= end currentDate = DateTimeHelper.AddDays(range.End, dayInterval); } else { //Moving backwards. currentDate = DateTimeHelper.AddDays(range.Start, dayInterval); } } while (currentDate != null && ((range = GetContainingDateRange((DateTime)currentDate)) != null)); return currentDate; } #endregion Public Methods #region Protected Methods /// /// All the items in the collection are removed. /// protected override void ClearItems() { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } foreach (CalendarDateRange item in Items) { UnRegisterItem(item); } base.ClearItems(); this._owner.UpdateCellItems(); } /// /// The item is inserted in the specified place in the collection. /// /// /// protected override void InsertItem(int index, CalendarDateRange item) { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } if (IsValid(item)) { RegisterItem(item); base.InsertItem(index, item); _owner.UpdateCellItems(); } else { throw new ArgumentOutOfRangeException(SR.Get(SRID.Calendar_UnSelectableDates)); } } /// /// The item in the specified index is removed from the collection. /// /// protected override void RemoveItem(int index) { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } if (index >= 0 && index < this.Count) { UnRegisterItem(Items[index]); } base.RemoveItem(index); _owner.UpdateCellItems(); } /// /// The object in the specified index is replaced with the provided item. /// /// /// protected override void SetItem(int index, CalendarDateRange item) { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } if (IsValid(item)) { CalendarDateRange oldItem = null; if (index >= 0 && index < this.Count) { oldItem = Items[index]; } base.SetItem(index, item); UnRegisterItem(oldItem); RegisterItem(Items[index]); _owner.UpdateCellItems(); } else { throw new ArgumentOutOfRangeException(SR.Get(SRID.Calendar_UnSelectableDates)); } } #endregion Protected Methods #region Private Methods /// /// Registers for change notification on date ranges /// /// private void RegisterItem(CalendarDateRange item) { if (item != null) { item.Changing += new EventHandler(Item_Changing); item.PropertyChanged += new PropertyChangedEventHandler(Item_PropertyChanged); } } /// /// Un registers for change notification on date ranges /// private void UnRegisterItem(CalendarDateRange item) { if (item != null) { item.Changing -= new EventHandler(Item_Changing); item.PropertyChanged -= new PropertyChangedEventHandler(Item_PropertyChanged); } } /// /// Reject date range changes that would make the blackout dates collection invalid /// /// /// private void Item_Changing(object sender, CalendarDateRangeChangingEventArgs e) { CalendarDateRange item = sender as CalendarDateRange; if (item != null) { if (!IsValid(e.Start, e.End)) { throw new ArgumentOutOfRangeException(SR.Get(SRID.Calendar_UnSelectableDates)); } } } /// /// Update the calendar view to reflect the new blackout dates /// /// /// private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (sender is CalendarDateRange) { _owner.UpdateCellItems(); } } /// /// Tests to see if a date range is not already selected /// /// date range to test /// True if no selected day falls in the given date range private bool IsValid(CalendarDateRange item) { return IsValid(item.Start, item.End); } /// /// Tests to see if a date range is not already selected /// /// First day of date range to test /// Last day of date range to test /// True if no selected day falls between start and end private bool IsValid(DateTime start, DateTime end) { foreach (object child in _owner.SelectedDates) { DateTime? day = child as DateTime?; Debug.Assert(day != null); if (DateTimeHelper.InRange(day.Value, start, end)) { return false; } } return true; } private bool IsValidThread() { return Thread.CurrentThread == this._dispatcherThread; } /// /// Gets the DateRange that contains the date. /// /// /// private CalendarDateRange GetContainingDateRange(DateTime date) { if (date == null) return null; for (int i = 0; i < Count; i++) { if (DateTimeHelper.InRange(date, this[i])) { return this[i]; } } return null; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarButton.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Data; using Microsoft.Windows.Automation.Peers; namespace Microsoft.Windows.Controls.Primitives { /// /// Represents a button control used in Calendar Control, which reacts to the Click event. /// [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateUnselected, GroupName = VisualStates.GroupSelection)] [TemplateVisualState(Name = VisualStates.StateSelected, GroupName = VisualStates.GroupSelection)] [TemplateVisualState(Name = VisualStates.StateCalendarButtonUnfocused, GroupName = VisualStates.GroupCalendarButtonFocus)] [TemplateVisualState(Name = VisualStates.StateCalendarButtonFocused, GroupName = VisualStates.GroupCalendarButtonFocus)] [TemplateVisualState(Name = VisualStates.StateInactive, GroupName = VisualStates.GroupActive)] [TemplateVisualState(Name = VisualStates.StateActive, GroupName = VisualStates.GroupActive)] public sealed class CalendarButton : Button { #region Data private bool _shouldCoerceContent; private object _coercedContent; #endregion Data /// /// Static constructor /// static CalendarButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CalendarButton), new FrameworkPropertyMetadata(typeof(CalendarButton))); ContentProperty.OverrideMetadata(typeof(CalendarButton), new FrameworkPropertyMetadata(null, OnCoerceContent)); } /// /// Represents the CalendarButton that is used in Calendar Control. /// public CalendarButton() : base() { // Attach the necessary events to their virtual counterparts Loaded += delegate { ChangeVisualState(false); }; } #region Public Properties #region HasSelectedDays internal static readonly DependencyPropertyKey HasSelectedDaysPropertyKey = DependencyProperty.RegisterReadOnly( "HasSelectedDays", typeof(bool), typeof(CalendarButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for HasSelectedDays property /// public static readonly DependencyProperty HasSelectedDaysProperty = HasSelectedDaysPropertyKey.DependencyProperty; /// /// True if the CalendarButton represents a date range containing the display date /// public bool HasSelectedDays { get { return (bool)GetValue(HasSelectedDaysProperty); } internal set { SetValue(HasSelectedDaysPropertyKey, value); } } #endregion HasSelectedDays #region IsInactive internal static readonly DependencyPropertyKey IsInactivePropertyKey = DependencyProperty.RegisterReadOnly( "IsInactive", typeof(bool), typeof(CalendarButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for IsInactive property /// public static readonly DependencyProperty IsInactiveProperty = IsInactivePropertyKey.DependencyProperty; /// /// True if the CalendarButton represents /// a month that falls outside the current year /// or /// a year that falls outside the current decade /// public bool IsInactive { get { return (bool)GetValue(IsInactiveProperty); } internal set { SetValue(IsInactivePropertyKey, value); } } #endregion IsInactive #endregion Public Properties #region Internal Properties internal Microsoft.Windows.Controls.Calendar Owner { get; set; } #endregion Internal Properties #region Public Methods /// /// Apply a template to the button. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); // Sync the logical and visual states of the control ChangeVisualState(false); } #endregion Public Methods #region Protected Methods /// /// Creates the automation peer for the DayButton. /// /// protected override AutomationPeer OnCreateAutomationPeer() { return new CalendarButtonAutomationPeer(this); } protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e) { ChangeVisualState(true); base.OnGotKeyboardFocus(e); } protected override void OnLostKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e) { ChangeVisualState(true); base.OnLostKeyboardFocus(e); } #endregion Protected Methods #region Internal Methods internal void SetContentInternal(string value) { if (BindingOperations.GetBindingExpressionBase(this, ContentControl.ContentProperty) != null) { Content = value; } else { this._shouldCoerceContent = true; this._coercedContent = value; this.CoerceValue(ContentControl.ContentProperty); } } #endregion #region Private Methods /// /// Change to the correct visual state for the button. /// /// /// true to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// private void ChangeVisualState(bool useTransitions) { // Update the SelectionStates group if (HasSelectedDays) { VisualStates.GoToState(this, useTransitions, VisualStates.StateSelected, VisualStates.StateUnselected); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateUnselected); } // Update the ActiveStates group if (IsInactive) { VisualStates.GoToState(this, useTransitions, VisualStates.StateInactive); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateActive, VisualStates.StateInactive); } // Update the FocusStates group if (IsKeyboardFocused) { VisualStates.GoToState(this, useTransitions, VisualStates.StateCalendarButtonFocused, VisualStates.StateCalendarButtonUnfocused); } else { VisualStateManager.GoToState(this, VisualStates.StateCalendarButtonUnfocused, useTransitions); } } /// /// Common PropertyChangedCallback for dependency properties that trigger visual state changes /// /// /// private static void OnVisualStatePropertyChanged(DependencyObject dObject, DependencyPropertyChangedEventArgs e) { CalendarButton button = dObject as CalendarButton; if (button != null && !object.Equals(e.OldValue, e.NewValue)) { button.ChangeVisualState(true); } } private static object OnCoerceContent(DependencyObject sender, object baseValue) { CalendarButton button = (CalendarButton)sender; if (button._shouldCoerceContent) { button._shouldCoerceContent = false; return button._coercedContent; } return baseValue; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarDateChangedEventArgs.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; namespace Microsoft.Windows.Controls { /// /// Provides data for the DateSelected and DisplayDateChanged events. /// public class CalendarDateChangedEventArgs : System.Windows.RoutedEventArgs { internal CalendarDateChangedEventArgs(DateTime? removedDate, DateTime? addedDate) { this.RemovedDate = removedDate; this.AddedDate = addedDate; } /// /// Gets the date to be newly displayed. /// public DateTime? AddedDate { get; private set; } /// /// Gets the date that was previously displayed. /// public DateTime? RemovedDate { get; private set; } } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarDateRange.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; namespace Microsoft.Windows.Controls { /// /// Specifies a DateTime range class which has a start and end. /// public sealed class CalendarDateRange : INotifyPropertyChanged { #region Data private DateTime _end; private DateTime _start; #endregion Data /// /// Initializes a new instance of the CalendarDateRange class. /// public CalendarDateRange() : this(DateTime.MinValue, DateTime.MaxValue) { } /// /// Initializes a new instance of the CalendarDateRange class which creates a range from a single DateTime value. /// /// public CalendarDateRange(DateTime day) : this(day, day) { } /// /// Initializes a new instance of the CalendarDateRange class which accepts range start and end dates. /// /// /// public CalendarDateRange(DateTime start, DateTime end) { _start = start; _end = end; } #region Public Events public event PropertyChangedEventHandler PropertyChanged; #endregion #region Public Properties /// /// Specifies the End date of the CalendarDateRange. /// public DateTime End { get { return CoerceEnd(_start, _end); } set { DateTime newEnd = CoerceEnd(_start, value); if (newEnd != End) { OnChanging(new CalendarDateRangeChangingEventArgs(_start, newEnd)); _end = value; OnPropertyChanged(new PropertyChangedEventArgs("End")); } } } /// /// Specifies the Start date of the CalendarDateRange. /// public DateTime Start { get { return _start; } set { if (_start != value) { DateTime oldEnd = End; DateTime newEnd = CoerceEnd(value, _end); OnChanging(new CalendarDateRangeChangingEventArgs(value, newEnd)); _start = value; OnPropertyChanged(new PropertyChangedEventArgs("Start")); if (newEnd != oldEnd) { OnPropertyChanged(new PropertyChangedEventArgs("End")); } } } } #endregion Public Properties #region Internal Events internal event EventHandler Changing; #endregion Internal Events #region Internal Methods /// /// Returns true if any day in the given DateTime range is contained in the current CalendarDateRange. /// /// /// internal bool ContainsAny(CalendarDateRange range) { return (range.End >= this.Start) && (this.End >= range.Start); } #endregion Internal Methods #region Private Methods private void OnChanging(CalendarDateRangeChangingEventArgs e) { EventHandler handler = this.Changing; if (handler != null) { handler(this, e); } } private void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler handler = this.PropertyChanged; if (handler != null) { handler(this, e); } } /// /// Coerced the end parameter to satisfy the start <= end constraint /// /// /// /// If start <= end the end parameter otherwise the start parameter private static DateTime CoerceEnd(DateTime start, DateTime end) { return (DateTime.Compare(start, end) <= 0) ? end : start; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarDateRangeChangingEventArgs.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; namespace Microsoft.Windows.Controls { /// /// Event arguments to notify clients that the range is changing and what the new range will be /// internal class CalendarDateRangeChangingEventArgs : EventArgs { public CalendarDateRangeChangingEventArgs(DateTime start, DateTime end) { _start = start; _end = end; } public DateTime Start { get { return _start; } } public DateTime End { get { return _end; } } private DateTime _start; private DateTime _end; } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarDayButton.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Data; using Microsoft.Windows.Automation.Peers; namespace Microsoft.Windows.Controls.Primitives { /// /// Represents a button control used in Calendar Control, which reacts to the Click event. /// [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateUnselected, GroupName = VisualStates.GroupSelection)] [TemplateVisualState(Name = VisualStates.StateSelected, GroupName = VisualStates.GroupSelection)] [TemplateVisualState(Name = VisualStates.StateCalendarButtonUnfocused, GroupName = VisualStates.GroupCalendarButtonFocus)] [TemplateVisualState(Name = VisualStates.StateCalendarButtonFocused, GroupName = VisualStates.GroupCalendarButtonFocus)] [TemplateVisualState(Name = VisualStates.StateInactive, GroupName = VisualStates.GroupActive)] [TemplateVisualState(Name = VisualStates.StateActive, GroupName = VisualStates.GroupActive)] [TemplateVisualState(Name = CalendarDayButton.StateRegularDay, GroupName = CalendarDayButton.GroupDay)] [TemplateVisualState(Name = CalendarDayButton.StateToday, GroupName = CalendarDayButton.GroupDay)] [TemplateVisualState(Name = CalendarDayButton.StateNormalDay, GroupName = CalendarDayButton.GroupBlackout)] [TemplateVisualState(Name = CalendarDayButton.StateBlackoutDay, GroupName = CalendarDayButton.GroupBlackout)] public sealed class CalendarDayButton : Button { #region Constants /// /// Default content for the CalendarDayButton /// private const int DEFAULTCONTENT = 1; /// /// Identifies the Today state. /// internal const string StateToday = "Today"; /// /// Identifies the RegularDay state. /// internal const string StateRegularDay = "RegularDay"; /// /// Name of the Day state group. /// internal const string GroupDay = "DayStates"; /// /// Identifies the BlackoutDay state. /// internal const string StateBlackoutDay = "BlackoutDay"; /// /// Identifies the NormalDay state. /// internal const string StateNormalDay = "NormalDay"; /// /// Name of the BlackoutDay state group. /// internal const string GroupBlackout = "BlackoutDayStates"; #endregion Constants #region Data private bool _shouldCoerceContent; private object _coercedContent; #endregion Data /// /// Static constructor /// static CalendarDayButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CalendarDayButton), new FrameworkPropertyMetadata(typeof(CalendarDayButton))); ContentProperty.OverrideMetadata(typeof(CalendarDayButton), new FrameworkPropertyMetadata(null, OnCoerceContent)); } /// /// Represents the CalendarDayButton that is used in Calendar Control. /// public CalendarDayButton() : base() { // Attach the necessary events to their virtual counterparts Loaded += delegate { ChangeVisualState(false); }; } #region Public Properties #region IsToday internal static readonly DependencyPropertyKey IsTodayPropertyKey = DependencyProperty.RegisterReadOnly( "IsToday", typeof(bool), typeof(CalendarDayButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for IsToday property /// public static readonly DependencyProperty IsTodayProperty = IsTodayPropertyKey.DependencyProperty; /// /// True if the CalendarDayButton represents today /// public bool IsToday { get { return (bool)GetValue(IsTodayProperty); } } #endregion IsToday #region IsSelected internal static readonly DependencyPropertyKey IsSelectedPropertyKey = DependencyProperty.RegisterReadOnly( "IsSelected", typeof(bool), typeof(CalendarDayButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for IsSelected property /// public static readonly DependencyProperty IsSelectedProperty = IsSelectedPropertyKey.DependencyProperty; /// /// True if the CalendarDayButton is selected /// public bool IsSelected { get { return (bool)GetValue(IsSelectedProperty); } } #endregion IsSelected #region IsInactive internal static readonly DependencyPropertyKey IsInactivePropertyKey = DependencyProperty.RegisterReadOnly( "IsInactive", typeof(bool), typeof(CalendarDayButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for IsActive property /// public static readonly DependencyProperty IsInactiveProperty = IsInactivePropertyKey.DependencyProperty; /// /// True if the CalendarDayButton represents a day that falls in the currently displayed month /// public bool IsInactive { get { return (bool)GetValue(IsInactiveProperty); } } #endregion IsInactive #region IsBlackedOut internal static readonly DependencyPropertyKey IsBlackedOutPropertyKey = DependencyProperty.RegisterReadOnly( "IsBlackedOut", typeof(bool), typeof(CalendarDayButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for IsBlackedOut property /// public static readonly DependencyProperty IsBlackedOutProperty = IsBlackedOutPropertyKey.DependencyProperty; /// /// True if the CalendarDayButton represents a blackout date /// public bool IsBlackedOut { get { return (bool)GetValue(IsBlackedOutProperty); } } #endregion IsBlackedOut #region IsHighlighted internal static readonly DependencyPropertyKey IsHighlightedPropertyKey = DependencyProperty.RegisterReadOnly( "IsHighlighted", typeof(bool), typeof(CalendarDayButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnVisualStatePropertyChanged))); /// /// Dependency property field for IsHighlighted property /// public static readonly DependencyProperty IsHighlightedProperty = IsHighlightedPropertyKey.DependencyProperty; /// /// True if the CalendarDayButton represents a highlighted date /// public bool IsHighlighted { get { return (bool)GetValue(IsHighlightedProperty); } } #endregion IsHighlighted #endregion Public Properties #region Internal Properties internal Microsoft.Windows.Controls.Calendar Owner { get; set; } #endregion Internal Properties #region Public Methods /// /// Apply a template to the button. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); // Sync the logical and visual states of the control ChangeVisualState(false); } #endregion Public Methods #region Protected Methods /// /// Creates the automation peer for the CalendarDayButton. /// /// protected override AutomationPeer OnCreateAutomationPeer() { return new CalendarDayButtonAutomationPeer(this); } protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e) { ChangeVisualState(true); base.OnGotKeyboardFocus(e); } protected override void OnLostKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e) { ChangeVisualState(true); base.OnLostKeyboardFocus(e); } #endregion Protected Methods #region Internal Methods /// /// Change to the correct visual state for the button. /// /// /// true to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// internal void ChangeVisualState(bool useTransitions) { if (this.IsEnabled) { VisualStates.GoToState(this, useTransitions, VisualStates.StateNormal); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateDisabled); } // Update the SelectionStates group if (IsSelected || IsHighlighted) { VisualStates.GoToState(this, useTransitions, VisualStates.StateSelected, VisualStates.StateUnselected); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateUnselected); } // Update the ActiveStates group if (!IsInactive) { VisualStates.GoToState(this, useTransitions, VisualStates.StateActive, VisualStates.StateInactive); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateInactive); } // Update the DayStates group if (IsToday && this.Owner != null && this.Owner.IsTodayHighlighted) { VisualStates.GoToState(this, useTransitions, StateToday, StateRegularDay); } else { VisualStates.GoToState(this, useTransitions, StateRegularDay); } // Update the BlackoutDayStates group if (IsBlackedOut) { VisualStates.GoToState(this, useTransitions, StateBlackoutDay, StateNormalDay); } else { VisualStates.GoToState(this, useTransitions, StateNormalDay); } // Update the FocusStates group if (IsKeyboardFocused) { VisualStates.GoToState(this, useTransitions, VisualStates.StateCalendarButtonFocused, VisualStates.StateCalendarButtonUnfocused); } else { VisualStateManager.GoToState(this, VisualStates.StateCalendarButtonUnfocused, useTransitions); } } internal void SetContentInternal(string value) { if (BindingOperations.GetBindingExpressionBase(this, ContentControl.ContentProperty) != null) { Content = value; } else { this._shouldCoerceContent = true; this._coercedContent = value; this.CoerceValue(ContentControl.ContentProperty); } } #endregion Internal Methods #region Private Methods /// /// Common PropertyChangedCallback for dependency properties that trigger visual state changes /// /// /// private static void OnVisualStatePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { CalendarDayButton dayButton = sender as CalendarDayButton; if (dayButton != null) { dayButton.ChangeVisualState(true); } } private static object OnCoerceContent(DependencyObject sender, object baseValue) { CalendarDayButton button = (CalendarDayButton)sender; if (button._shouldCoerceContent) { button._shouldCoerceContent = false; return button._coercedContent; } return baseValue; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarItem.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; namespace Microsoft.Windows.Controls.Primitives { [TemplatePart(Name = CalendarItem.ElementRoot, Type = typeof(FrameworkElement))] [TemplatePart(Name = CalendarItem.ElementHeaderButton, Type = typeof(Button))] [TemplatePart(Name = CalendarItem.ElementPreviousButton, Type = typeof(Button))] [TemplatePart(Name = CalendarItem.ElementNextButton, Type = typeof(Button))] [TemplatePart(Name = CalendarItem.ElementDayTitleTemplate, Type = typeof(DataTemplate))] [TemplatePart(Name = CalendarItem.ElementMonthView, Type = typeof(Grid))] [TemplatePart(Name = CalendarItem.ElementYearView, Type = typeof(Grid))] [TemplatePart(Name = CalendarItem.ElementDisabledVisual, Type = typeof(FrameworkElement))] [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] public sealed partial class CalendarItem : Control { #region Constants private const string ElementRoot = "PART_Root"; private const string ElementHeaderButton = "PART_HeaderButton"; private const string ElementPreviousButton = "PART_PreviousButton"; private const string ElementNextButton = "PART_NextButton"; private const string ElementDayTitleTemplate = "DayTitleTemplate"; private const string ElementMonthView = "PART_MonthView"; private const string ElementYearView = "PART_YearView"; private const string ElementDisabledVisual = "PART_DisabledVisual"; private const int COLS = 7; private const int ROWS = 7; private const int YEAR_COLS = 4; private const int YEAR_ROWS = 3; private const int NUMBER_OF_DAYS_IN_WEEK = 7; #endregion Constants #region Data private System.Globalization.Calendar _calendar = new GregorianCalendar(); private DataTemplate _dayTitleTemplate; private FrameworkElement _disabledVisual; private Button _headerButton; private Grid _monthView; private Button _nextButton; private Button _previousButton; private Grid _yearView; private bool _isMonthPressed; private bool _isDayPressed; #endregion Data static CalendarItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CalendarItem), new FrameworkPropertyMetadata(typeof(CalendarItem))); FocusableProperty.OverrideMetadata(typeof(CalendarItem), new FrameworkPropertyMetadata(false)); KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(CalendarItem), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(CalendarItem), new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained)); } /// /// Represents the month that is used in Calendar Control. /// public CalendarItem() { } #region Internal Properties internal Grid MonthView { get { return _monthView; } } internal Microsoft.Windows.Controls.Calendar Owner { get; set; } internal Grid YearView { get { return _yearView; } } #endregion Internal Properties #region Private Properties /// /// Gets a value indicating whether the calendar is displayed in months, years or decades. /// private CalendarMode DisplayMode { get { return (this.Owner != null) ? this.Owner.DisplayMode : CalendarMode.Month; } } private Button HeaderButton { get { return this._headerButton; } } private Button NextButton { get { return this._nextButton; } } private Button PreviousButton { get { return this._previousButton; } } private DateTime DisplayDate { get { return (Owner != null) ? Owner.DisplayDate : DateTime.Today; } } #endregion Private Properties #region Public Methods /// /// Invoked whenever application code or an internal process, /// such as a rebuilding layout pass, calls the ApplyTemplate method. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); if (this._previousButton != null) { this._previousButton.Click -= new RoutedEventHandler(PreviousButton_Click); } if (this._nextButton != null) { this._nextButton.Click -= new RoutedEventHandler(NextButton_Click); } if (this._headerButton != null) { this._headerButton.Click -= new RoutedEventHandler(HeaderButton_Click); } _monthView = GetTemplateChild(ElementMonthView) as Grid; _yearView = GetTemplateChild(ElementYearView) as Grid; _previousButton = GetTemplateChild(ElementPreviousButton) as Button; _nextButton = GetTemplateChild(ElementNextButton) as Button; _headerButton = GetTemplateChild(ElementHeaderButton) as Button; _disabledVisual = GetTemplateChild(ElementDisabledVisual) as FrameworkElement; // WPF Compat: Unlike SL, WPF is not able to get elements in template resources with GetTemplateChild() _dayTitleTemplate = null; if (Template != null && Template.Resources.Contains(ElementDayTitleTemplate)) { _dayTitleTemplate = Template.Resources[ElementDayTitleTemplate] as DataTemplate; } if (this._previousButton != null) { // If the user does not provide a Content value in template, we provide a helper text that can be used in Accessibility // this text is not shown on the UI, just used for Accessibility purposes if (this._previousButton.Content == null) { this._previousButton.Content = SR.Get(SRID.Calendar_PreviousButtonName); } this._previousButton.Click += new RoutedEventHandler(PreviousButton_Click); } if (this._nextButton != null) { // If the user does not provide a Content value in template, we provide a helper text that can be used in Accessibility // this text is not shown on the UI, just used for Accessibility purposes if (this._nextButton.Content == null) { this._nextButton.Content = SR.Get(SRID.Calendar_NextButtonName); } this._nextButton.Click += new RoutedEventHandler(NextButton_Click); } if (this._headerButton != null) { this._headerButton.Click += new RoutedEventHandler(HeaderButton_Click); } PopulateGrids(); if (this.Owner != null) { switch (this.Owner.DisplayMode) { case CalendarMode.Year: UpdateYearMode(); break; case CalendarMode.Decade: UpdateDecadeMode(); break; case CalendarMode.Month: UpdateMonthMode(); break; default: Debug.Assert(false); break; } } else { UpdateMonthMode(); } } #endregion Public Methods #region Protected Methods protected override void OnMouseUp(MouseButtonEventArgs e) { base.OnMouseUp(e); if (this.IsMouseCaptured) { this.ReleaseMouseCapture(); } this._isMonthPressed = false; this._isDayPressed = false; // In Month mode, we may need to end a drag selection even if the mouse up isn't on the calendar. if (!e.Handled && this.Owner.DisplayMode == CalendarMode.Month && this.Owner.HoverEnd.HasValue) { FinishSelection(this.Owner.HoverEnd.Value); } } protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); if (!this.IsMouseCaptured) { this._isDayPressed = false; this._isMonthPressed = false; } } #endregion #region Internal Methods internal void UpdateDecadeMode() { DateTime selectedYear; if (this.Owner != null) { selectedYear = this.Owner.DisplayYear; } else { selectedYear = DateTime.Today; } int decade = GetDecadeForDecadeMode(selectedYear); int decadeEnd = decade + 9; SetDecadeModeHeaderButton(decade); SetDecadeModePreviousButton(decade); SetDecadeModeNextButton(decadeEnd); if (_yearView != null) { SetYearButtons(decade, decadeEnd); } } internal void UpdateMonthMode() { SetMonthModeHeaderButton(); SetMonthModePreviousButton(); SetMonthModeNextButton(); if (_monthView != null) { SetMonthModeDayTitles(); SetMonthModeCalendarDayButtons(); AddMonthModeHighlight(); } } internal void UpdateYearMode() { SetYearModeHeaderButton(); SetYearModePreviousButton(); SetYearModeNextButton(); if (_yearView != null) { SetYearModeMonthButtons(); } } internal IEnumerable GetCalendarDayButtons() { // TODO: should be updated if we support MultiCalendar int count = ROWS * COLS; if (MonthView != null) { UIElementCollection dayButtonsHost = MonthView.Children; for (int childIndex = COLS; childIndex < count; childIndex++) { CalendarDayButton b = dayButtonsHost[childIndex] as CalendarDayButton; if (b != null) { yield return b; } } } } internal CalendarDayButton GetFocusedCalendarDayButton() { foreach (CalendarDayButton b in GetCalendarDayButtons()) { if (b != null && b.IsFocused) { return b; } } return null; } internal CalendarDayButton GetCalendarDayButton(DateTime date) { foreach (CalendarDayButton b in GetCalendarDayButtons()) { if (b != null && b.DataContext is DateTime) { if (DateTimeHelper.CompareDays(date, (DateTime)b.DataContext) == 0) { return b; } } } return null; } internal CalendarButton GetCalendarButton(DateTime date, CalendarMode mode) { Debug.Assert(mode != CalendarMode.Month); foreach (CalendarButton b in GetCalendarButtons()) { if (b != null && b.DataContext is DateTime) { if (mode == CalendarMode.Year) { if (DateTimeHelper.CompareYearMonth(date, (DateTime)b.DataContext) == 0) { return b; } } else { if (date.Year == ((DateTime)b.DataContext).Year) { return b; } } } } return null; } internal CalendarButton GetFocusedCalendarButton() { foreach (CalendarButton b in GetCalendarButtons()) { if (b != null && b.IsFocused) { return b; } } return null; } private IEnumerable GetCalendarButtons() { foreach (UIElement element in this.YearView.Children) { CalendarButton b = element as CalendarButton; if (b != null) { yield return b; } } } internal void FocusDate(DateTime date) { FrameworkElement focusTarget = null; switch (this.DisplayMode) { case CalendarMode.Month: { focusTarget = GetCalendarDayButton(date); break; } case CalendarMode.Year: case CalendarMode.Decade: { focusTarget = GetCalendarButton(date, this.DisplayMode); break; } default: { Debug.Assert(false); break; } } if (focusTarget != null && !focusTarget.IsFocused) { focusTarget.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } } #endregion Internal Methods #region Private Methods private int GetDecadeForDecadeMode(DateTime selectedYear) { int decade = DateTimeHelper.DecadeOfDate(selectedYear); // Adjust the decade value if the mouse move selection is on, // such that if first or last year among the children are selected // then return the current selected decade as is. if (_isMonthPressed && _yearView != null) { UIElementCollection yearViewChildren = _yearView.Children; int count = yearViewChildren.Count; if (count > 0) { CalendarButton child = yearViewChildren[0] as CalendarButton; if (child != null && child.DataContext is DateTime && ((DateTime)child.DataContext).Year == selectedYear.Year) { return (decade + 10); } } if (count > 1) { CalendarButton child = yearViewChildren[count - 1] as CalendarButton; if (child != null && child.DataContext is DateTime && ((DateTime)child.DataContext).Year == selectedYear.Year) { return (decade - 10); } } } return decade; } private void EndDrag(bool ctrl, DateTime selectedDate) { if (this.Owner != null) { this.Owner.CurrentDate = selectedDate; if (this.Owner.HoverStart.HasValue) { if ( ctrl && DateTime.Compare(this.Owner.HoverStart.Value, selectedDate) == 0 && (Owner.SelectionMode == CalendarSelectionMode.SingleDate || Owner.SelectionMode == CalendarSelectionMode.MultipleRange)) { // Ctrl + single click = toggle this.Owner.SelectedDates.Toggle(selectedDate); } else { // this is selection with Mouse, we do not guarantee the range does not include BlackOutDates. // Use the internal AddRange that omits BlackOutDates based on the SelectionMode this.Owner.SelectedDates.AddRangeInternal(this.Owner.HoverStart.Value, selectedDate); } Owner.OnDayClick(selectedDate); } } } private void CellOrMonth_PreviewKeyDown(object sender, RoutedEventArgs e) { Debug.Assert(e != null); if (this.Owner == null) { return; } this.Owner.OnDayOrMonthPreviewKeyDown(e); } private void Cell_Clicked(object sender, RoutedEventArgs e) { if (this.Owner == null) { return; } CalendarDayButton b = sender as CalendarDayButton; Debug.Assert(b != null); if (!(b.DataContext is DateTime)) { return; } // If the day is a blackout day selection is not allowed if (!b.IsBlackedOut) { DateTime clickedDate = (DateTime)b.DataContext; bool ctrl, shift; KeyboardHelper.GetMetaKeyState(out ctrl, out shift); switch (this.Owner.SelectionMode) { case CalendarSelectionMode.None: { break; } case CalendarSelectionMode.SingleDate: { if (!ctrl) { this.Owner.SelectedDate = clickedDate; } else { this.Owner.SelectedDates.Toggle(clickedDate); } break; } case CalendarSelectionMode.SingleRange: { DateTime? lastDate = this.Owner.CurrentDate; this.Owner.SelectedDates.ClearInternal(true /*fireChangeNotification*/); if (shift && lastDate.HasValue) { this.Owner.SelectedDates.AddRangeInternal(lastDate.Value, clickedDate); } else { this.Owner.SelectedDate = clickedDate; this.Owner.HoverStart = null; this.Owner.HoverEnd = null; } break; } case CalendarSelectionMode.MultipleRange: { if (!ctrl) { this.Owner.SelectedDates.ClearInternal(true /*fireChangeNotification*/); } if (shift) { this.Owner.SelectedDates.AddRangeInternal(this.Owner.CurrentDate, clickedDate); } else { if (!ctrl) { this.Owner.SelectedDate = clickedDate; } else { this.Owner.SelectedDates.Toggle(clickedDate); this.Owner.HoverStart = null; this.Owner.HoverEnd = null; } } break; } } this.Owner.OnDayClick(clickedDate); } } private void Cell_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { CalendarDayButton b = sender as CalendarDayButton; if (b == null) { return; } if (this.Owner == null || !(b.DataContext is DateTime)) { return; } if (b.IsBlackedOut) { this.Owner.HoverStart = null; } else { this._isDayPressed = true; Mouse.Capture(this, CaptureMode.SubTree); b.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); bool ctrl, shift; KeyboardHelper.GetMetaKeyState(out ctrl, out shift); DateTime selectedDate = (DateTime)b.DataContext; Debug.Assert(selectedDate != null); switch (this.Owner.SelectionMode) { case CalendarSelectionMode.None: { break; } case CalendarSelectionMode.SingleDate: { this.Owner.DatePickerDisplayDateFlag = true; if (!ctrl) { this.Owner.SelectedDate = selectedDate; } else { this.Owner.SelectedDates.Toggle(selectedDate); } break; } case CalendarSelectionMode.SingleRange: { this.Owner.SelectedDates.ClearInternal(); if (shift) { if (!this.Owner.HoverStart.HasValue) { this.Owner.HoverStart = this.Owner.HoverEnd = this.Owner.CurrentDate; } } else { this.Owner.HoverStart = this.Owner.HoverEnd = selectedDate; } break; } case CalendarSelectionMode.MultipleRange: { if (!ctrl) { this.Owner.SelectedDates.ClearInternal(); } if (shift) { if (!this.Owner.HoverStart.HasValue) { this.Owner.HoverStart = this.Owner.HoverEnd = this.Owner.CurrentDate; } } else { this.Owner.HoverStart = this.Owner.HoverEnd = selectedDate; } break; } } this.Owner.CurrentDate = selectedDate; this.Owner.UpdateCellItems(); } } private void Cell_MouseEnter(object sender, MouseEventArgs e) { CalendarDayButton b = sender as CalendarDayButton; if (b == null) { return; } if (b.IsBlackedOut) { return; } if (e.LeftButton == MouseButtonState.Pressed && this._isDayPressed) { b.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); if (this.Owner == null || !(b.DataContext is DateTime)) { return; } DateTime selectedDate = (DateTime)b.DataContext; switch (this.Owner.SelectionMode) { case CalendarSelectionMode.SingleDate: { this.Owner.DatePickerDisplayDateFlag = true; this.Owner.HoverStart = this.Owner.HoverEnd = null; if (this.Owner.SelectedDates.Count == 0) { this.Owner.SelectedDates.Add(selectedDate); } else { this.Owner.SelectedDates[0] = selectedDate; } return; } } this.Owner.HoverEnd = selectedDate; this.Owner.CurrentDate = selectedDate; this.Owner.UpdateCellItems(); } } private void Cell_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { CalendarDayButton b = sender as CalendarDayButton; if (b == null) { return; } if (this.Owner == null) { return; } if (!b.IsBlackedOut) { this.Owner.OnDayButtonMouseUp(e); } if (!(b.DataContext is DateTime)) { return; } FinishSelection((DateTime)b.DataContext); e.Handled = true; } private void FinishSelection(DateTime selectedDate) { bool ctrl, shift; KeyboardHelper.GetMetaKeyState(out ctrl, out shift); if (this.Owner.SelectionMode == CalendarSelectionMode.None || this.Owner.SelectionMode == CalendarSelectionMode.SingleDate) { this.Owner.OnDayClick(selectedDate); return; } if (this.Owner.HoverStart.HasValue) { switch (this.Owner.SelectionMode) { case CalendarSelectionMode.SingleRange: { // Update SelectedDates this.Owner.SelectedDates.ClearInternal(); EndDrag(ctrl, selectedDate); break; } case CalendarSelectionMode.MultipleRange: { // add the selection (either single day or SingleRange day) EndDrag(ctrl, selectedDate); break; } } } else { // If the day is blacked out but also a trailing day we should be able to switch months CalendarDayButton b = GetCalendarDayButton(selectedDate); if (b != null && b.IsInactive && b.IsBlackedOut) { this.Owner.OnDayClick(selectedDate); } } } private void Month_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { CalendarButton b = sender as CalendarButton; if (b != null) { this._isMonthPressed = true; Mouse.Capture(this, CaptureMode.SubTree); if (this.Owner != null) { this.Owner.OnCalendarButtonPressed(b, false); } } } private void Month_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { CalendarButton b = sender as CalendarButton; if (b != null && this.Owner != null) { this.Owner.OnCalendarButtonPressed(b, true); } } private void Month_MouseEnter(object sender, MouseEventArgs e) { CalendarButton b = sender as CalendarButton; if (b != null) { if (this._isMonthPressed && this.Owner != null) { this.Owner.OnCalendarButtonPressed(b, false); } } } private void Month_Clicked(object sender, RoutedEventArgs e) { CalendarButton b = sender as CalendarButton; if (b != null) { this.Owner.OnCalendarButtonPressed(b, true); } } private void HeaderButton_Click(object sender, RoutedEventArgs e) { if (this.Owner != null) { if (this.Owner.DisplayMode == CalendarMode.Month) { this.Owner.DisplayMode = CalendarMode.Year; } else { Debug.Assert(this.Owner.DisplayMode == CalendarMode.Year); this.Owner.DisplayMode = CalendarMode.Decade; } this.FocusDate(this.DisplayDate); } } private void PreviousButton_Click(object sender, RoutedEventArgs e) { if (this.Owner != null) { this.Owner.OnPreviousClick(); } } private void NextButton_Click(object sender, RoutedEventArgs e) { if (this.Owner != null) { this.Owner.OnNextClick(); } } private void PopulateGrids() { if (_monthView != null) { if (_dayTitleTemplate != null) { for (int i = 0; i < COLS; i++) { FrameworkElement titleCell = (FrameworkElement)this._dayTitleTemplate.LoadContent(); titleCell.SetValue(Grid.RowProperty, 0); titleCell.SetValue(Grid.ColumnProperty, i); this._monthView.Children.Add(titleCell); } } for (int i = 1; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { CalendarDayButton dayCell = new CalendarDayButton(); dayCell.Owner = this.Owner; dayCell.SetValue(Grid.RowProperty, i); dayCell.SetValue(Grid.ColumnProperty, j); dayCell.SetBinding(CalendarDayButton.StyleProperty, GetOwnerBinding("CalendarDayButtonStyle")); dayCell.AddHandler(CalendarDayButton.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Cell_MouseLeftButtonDown), true); dayCell.AddHandler(CalendarDayButton.MouseLeftButtonUpEvent, new MouseButtonEventHandler(Cell_MouseLeftButtonUp), true); dayCell.AddHandler(CalendarDayButton.MouseEnterEvent, new MouseEventHandler(Cell_MouseEnter), true); dayCell.Click += new RoutedEventHandler(Cell_Clicked); dayCell.AddHandler(PreviewKeyDownEvent, new RoutedEventHandler(CellOrMonth_PreviewKeyDown), true); this._monthView.Children.Add(dayCell); } } } if (_yearView != null) { CalendarButton monthCell; int count = 0; for (int i = 0; i < YEAR_ROWS; i++) { for (int j = 0; j < YEAR_COLS; j++) { monthCell = new CalendarButton(); monthCell.Owner = this.Owner; monthCell.SetValue(Grid.RowProperty, i); monthCell.SetValue(Grid.ColumnProperty, j); monthCell.SetBinding(CalendarButton.StyleProperty, GetOwnerBinding("CalendarButtonStyle")); monthCell.AddHandler(CalendarButton.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Month_MouseLeftButtonDown), true); monthCell.AddHandler(CalendarButton.MouseLeftButtonUpEvent, new MouseButtonEventHandler(Month_MouseLeftButtonUp), true); monthCell.AddHandler(CalendarButton.MouseEnterEvent, new MouseEventHandler(Month_MouseEnter), true); monthCell.AddHandler(UIElement.PreviewKeyDownEvent, new RoutedEventHandler(CellOrMonth_PreviewKeyDown), true); monthCell.Click += new RoutedEventHandler(Month_Clicked); this._yearView.Children.Add(monthCell); count++; } } } } #region Month Mode Display private void SetMonthModeDayTitles() { if (_monthView != null) { string[] shortestDayNames = DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)).ShortestDayNames; for (int childIndex = 0; childIndex < COLS; childIndex++) { FrameworkElement daytitle = _monthView.Children[childIndex] as FrameworkElement; if (daytitle != null && shortestDayNames != null && shortestDayNames.Length > 0) { if (this.Owner != null) { daytitle.DataContext = shortestDayNames[(childIndex + (int)this.Owner.FirstDayOfWeek) % shortestDayNames.Length]; } else { daytitle.DataContext = shortestDayNames[(childIndex + (int)DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)).FirstDayOfWeek) % shortestDayNames.Length]; } } } } } private void SetMonthModeCalendarDayButtons() { DateTime firstDayOfMonth = DateTimeHelper.DiscardDayTime(DisplayDate); int lastMonthToDisplay = GetNumberOfDisplayedDaysFromPreviousMonth(firstDayOfMonth); bool isMinMonth = DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MinValue) <= 0; bool isMaxMonth = DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MaxValue) >= 0; int daysInMonth = _calendar.GetDaysInMonth(firstDayOfMonth.Year, firstDayOfMonth.Month); CultureInfo culture = DateTimeHelper.GetCulture(this); int count = ROWS * COLS; for (int childIndex = COLS; childIndex < count; childIndex++) { CalendarDayButton childButton = _monthView.Children[childIndex] as CalendarDayButton; Debug.Assert(childButton != null); int dayOffset = childIndex - lastMonthToDisplay - COLS; if ((!isMinMonth || (dayOffset >= 0)) && (!isMaxMonth || (dayOffset < daysInMonth))) { DateTime dateToAdd = _calendar.AddDays(firstDayOfMonth, dayOffset); SetMonthModeDayButtonState(childButton, dateToAdd); childButton.DataContext = dateToAdd; childButton.SetContentInternal(DateTimeHelper.ToDayString(dateToAdd, culture)); } else { SetMonthModeDayButtonState(childButton, null); childButton.DataContext = null; childButton.SetContentInternal(DateTimeHelper.ToDayString(null, culture)); } } } private void SetMonthModeDayButtonState(CalendarDayButton childButton, DateTime? dateToAdd) { if (this.Owner != null) { if (dateToAdd.HasValue) { childButton.Visibility = Visibility.Visible; // If the day is outside the DisplayDateStart/End boundary, do not show it if (DateTimeHelper.CompareDays(dateToAdd.Value, this.Owner.DisplayDateStartInternal) < 0 || DateTimeHelper.CompareDays(dateToAdd.Value, this.Owner.DisplayDateEndInternal) > 0) { childButton.IsEnabled = false; childButton.Visibility = Visibility.Hidden; } else { childButton.IsEnabled = true; // SET IF THE DAY IS SELECTABLE OR NOT childButton.SetValue( CalendarDayButton.IsBlackedOutPropertyKey, this.Owner.BlackoutDates.Contains(dateToAdd.Value)); // SET IF THE DAY IS ACTIVE OR NOT: set if the day is a trailing day or not childButton.SetValue( CalendarDayButton.IsInactivePropertyKey, DateTimeHelper.CompareYearMonth(dateToAdd.Value, this.Owner.DisplayDateInternal) != 0); // SET IF THE DAY IS TODAY OR NOT if (DateTimeHelper.CompareDays(dateToAdd.Value, DateTime.Today) == 0) { childButton.SetValue(CalendarDayButton.IsTodayPropertyKey, true); // Calendar.IsTodayHighlighted affects the final visual state for Today buttons // but childButton property change callbacks are no called in response to // Calendar.IsTodayHighlighted changing so we must explicitly update the visual state childButton.ChangeVisualState(true); } else { childButton.SetValue(CalendarDayButton.IsTodayPropertyKey, false); } // SET IF THE DAY IS SELECTED OR NOT // Since we should be comparing the Date values not DateTime values, we can't use this.Owner.SelectedDates.Contains(dateToAdd) directly bool isSelected = false; foreach (DateTime item in this.Owner.SelectedDates) { isSelected |= (DateTimeHelper.CompareDays(dateToAdd.Value, item) == 0); } childButton.SetValue(CalendarDayButton.IsSelectedPropertyKey, isSelected); } } else { childButton.Visibility = Visibility.Hidden; childButton.IsEnabled = false; childButton.SetValue(CalendarDayButton.IsBlackedOutPropertyKey, false); childButton.SetValue(CalendarDayButton.IsInactivePropertyKey, true); childButton.SetValue(CalendarDayButton.IsTodayPropertyKey, false); childButton.SetValue(CalendarDayButton.IsSelectedPropertyKey, false); } } } private void AddMonthModeHighlight() { var owner = this.Owner; if (owner == null) { return; } if (owner.HoverStart.HasValue && owner.HoverEnd.HasValue) { DateTime hStart = owner.HoverEnd.Value; DateTime hEnd = owner.HoverEnd.Value; int daysToHighlight = DateTimeHelper.CompareDays(owner.HoverEnd.Value, owner.HoverStart.Value); if (daysToHighlight < 0) { hEnd = this.Owner.HoverStart.Value; } else { hStart = this.Owner.HoverStart.Value; } int count = ROWS * COLS; for (int childIndex = COLS; childIndex < count; childIndex++) { CalendarDayButton childButton = _monthView.Children[childIndex] as CalendarDayButton; if (childButton.DataContext is DateTime) { DateTime date = (DateTime)childButton.DataContext; childButton.SetValue( CalendarDayButton.IsHighlightedPropertyKey, (daysToHighlight != 0) && DateTimeHelper.InRange(date, hStart, hEnd)); } else { childButton.SetValue(CalendarDayButton.IsHighlightedPropertyKey, false); } } } else { int count = ROWS * COLS; for (int childIndex = COLS; childIndex < count; childIndex++) { CalendarDayButton childButton = _monthView.Children[childIndex] as CalendarDayButton; childButton.SetValue(CalendarDayButton.IsHighlightedPropertyKey, false); } } } private void SetMonthModeHeaderButton() { if (this._headerButton != null) { this._headerButton.Content = DateTimeHelper.ToYearMonthPatternString(DisplayDate, DateTimeHelper.GetCulture(this)); if (this.Owner != null) { this._headerButton.IsEnabled = true; } } } private void SetMonthModeNextButton() { if (this.Owner != null && _nextButton != null) { DateTime firstDayOfMonth = DateTimeHelper.DiscardDayTime(DisplayDate); // DisplayDate is equal to DateTime.MaxValue if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MaxValue) == 0) { _nextButton.IsEnabled = false; } else { // Since we are sure DisplayDate is not equal to DateTime.MaxValue, // it is safe to use AddMonths DateTime firstDayOfNextMonth = _calendar.AddMonths(firstDayOfMonth, 1); _nextButton.IsEnabled = (DateTimeHelper.CompareDays(this.Owner.DisplayDateEndInternal, firstDayOfNextMonth) > -1); } } } private void SetMonthModePreviousButton() { if (this.Owner != null && _previousButton != null) { DateTime firstDayOfMonth = DateTimeHelper.DiscardDayTime(DisplayDate); _previousButton.IsEnabled = (DateTimeHelper.CompareDays(this.Owner.DisplayDateStartInternal, firstDayOfMonth) < 0); } } #endregion #region Year Mode Display private void SetYearButtons(int decade, int decadeEnd) { int year; int count = -1; foreach (object child in _yearView.Children) { CalendarButton childButton = child as CalendarButton; Debug.Assert(childButton != null); year = decade + count; if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year) { // There should be no time component. Time is 12:00 AM DateTime day = new DateTime(year, 1, 1); childButton.DataContext = day; childButton.SetContentInternal(DateTimeHelper.ToYearString(day, DateTimeHelper.GetCulture(this))); childButton.Visibility = Visibility.Visible; if (this.Owner != null) { childButton.HasSelectedDays = (Owner.DisplayDate.Year == year); if (year < this.Owner.DisplayDateStartInternal.Year || year > this.Owner.DisplayDateEndInternal.Year) { childButton.IsEnabled = false; childButton.Opacity = 0; } else { childButton.IsEnabled = true; childButton.Opacity = 1; } } // SET IF THE YEAR IS INACTIVE OR NOT: set if the year is a trailing year or not childButton.IsInactive = year < decade || year > decadeEnd; } else { childButton.DataContext = null; childButton.IsEnabled = false; childButton.Opacity = 0; } count++; } } private void SetYearModeMonthButtons() { int count = 0; foreach (object child in _yearView.Children) { CalendarButton childButton = child as CalendarButton; Debug.Assert(childButton != null); // There should be no time component. Time is 12:00 AM DateTime day = new DateTime(DisplayDate.Year, count + 1, 1); childButton.DataContext = day; childButton.SetContentInternal(DateTimeHelper.ToAbbreviatedMonthString(day,DateTimeHelper.GetCulture(this))); childButton.Visibility = Visibility.Visible; if (this.Owner != null) { Debug.Assert(this.Owner.DisplayDateInternal != null); childButton.HasSelectedDays = (DateTimeHelper.CompareYearMonth(day, this.Owner.DisplayDateInternal) == 0); if (DateTimeHelper.CompareYearMonth(day, this.Owner.DisplayDateStartInternal) < 0 || DateTimeHelper.CompareYearMonth(day, this.Owner.DisplayDateEndInternal) > 0) { childButton.IsEnabled = false; childButton.Opacity = 0; } else { childButton.IsEnabled = true; childButton.Opacity = 1; } } childButton.IsInactive = false; count++; } } private void SetYearModeHeaderButton() { if (this._headerButton != null) { this._headerButton.IsEnabled = true; this._headerButton.Content = DateTimeHelper.ToYearString(DisplayDate, DateTimeHelper.GetCulture(this)); } } private void SetYearModeNextButton() { if (this.Owner != null && _nextButton != null) { _nextButton.IsEnabled = (this.Owner.DisplayDateEndInternal.Year != DisplayDate.Year); } } private void SetYearModePreviousButton() { if (this.Owner != null && _previousButton != null) { _previousButton.IsEnabled = (this.Owner.DisplayDateStartInternal.Year != DisplayDate.Year); } } #endregion Year Mode Display #region Decade Mode Display private void SetDecadeModeHeaderButton(int decade) { if (this._headerButton != null) { this._headerButton.Content = DateTimeHelper.ToDecadeRangeString(decade, DateTimeHelper.GetCulture(this)); this._headerButton.IsEnabled = false; } } private void SetDecadeModeNextButton(int decadeEnd) { if (this.Owner != null && _nextButton != null) { _nextButton.IsEnabled = (this.Owner.DisplayDateEndInternal.Year > decadeEnd); } } private void SetDecadeModePreviousButton(int decade) { if (this.Owner != null && _previousButton != null) { _previousButton.IsEnabled = (decade > this.Owner.DisplayDateStartInternal.Year); } } #endregion Decade Mode Display // How many days of the previous month need to be displayed private int GetNumberOfDisplayedDaysFromPreviousMonth(DateTime firstOfMonth) { DayOfWeek day = _calendar.GetDayOfWeek(firstOfMonth); int i; if (this.Owner != null) { i = ((day - this.Owner.FirstDayOfWeek + NUMBER_OF_DAYS_IN_WEEK) % NUMBER_OF_DAYS_IN_WEEK); } else { i = ((day - DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)).FirstDayOfWeek + NUMBER_OF_DAYS_IN_WEEK) % NUMBER_OF_DAYS_IN_WEEK); } if (i == 0) { return NUMBER_OF_DAYS_IN_WEEK; } else { return i; } } /// /// Gets a binding to a property on the owning calendar /// /// /// private BindingBase GetOwnerBinding(string propertyName) { Binding result = new Binding(propertyName); result.Source = this.Owner; return result; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarMode.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- namespace Microsoft.Windows.Controls { /// /// Specifies values for the different modes of operation of a Calendar. /// public enum CalendarMode { /// /// The Calendar displays a month at a time. /// Month = 0, /// /// The Calendar displays a year at a time. /// Year = 1, /// /// The Calendar displays a decade at a time. /// Decade = 2, } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarModeChangedEventArgs.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- namespace Microsoft.Windows.Controls { /// /// Provides data for the DisplayModeChanged event. /// public class CalendarModeChangedEventArgs : System.Windows.RoutedEventArgs { /// /// Initializes a new instance of the CalendarModeChangedEventArgs class. /// /// Previous value of the property, prior to the event being raised. /// Current value of the property at the time of the event. public CalendarModeChangedEventArgs(CalendarMode oldMode, CalendarMode newMode) { this.OldMode = oldMode; this.NewMode = newMode; } /// /// Gets the new mode of the Calendar. /// public CalendarMode NewMode { get; private set; } /// /// Gets the previous mode of the Calendar. /// public CalendarMode OldMode { get; private set; } } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarSelectionChangedEventArgs.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; namespace Microsoft.Windows.Controls { /// /// Workaround for Dev10 Bug 527138 UIElement.RaiseEvent(e) throws InvalidCastException when /// e is of type SelectionChangedEventArgs /// e.RoutedEvent was registered with a handler not of type System.Windows.Controls.SelectionChangedEventHandler /// internal class CalendarSelectionChangedEventArgs : SelectionChangedEventArgs { /// /// Constructor /// /// Routed Event /// Items removed from selection /// Items added to selection public CalendarSelectionChangedEventArgs(RoutedEvent eventId, IList removedItems, IList addedItems) : base(eventId, removedItems, addedItems) { } protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget) { EventHandler handler = genericHandler as EventHandler; if (handler != null) { handler(genericTarget, this); } else { base.InvokeEventHandler(genericHandler, genericTarget); } } } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/CalendarSelectionMode.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- namespace Microsoft.Windows.Controls { /// /// Specifies values for the different selection modes of a Calendar. /// public enum CalendarSelectionMode { /// /// One date can be selected at a time. /// SingleDate = 0, /// /// One range of dates can be selected at a time. /// SingleRange = 1, /// /// Multiple dates or ranges can be selected at a time. /// MultipleRange = 2, /// /// No dates can be selected. /// None = 3, } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/DateTimeHelper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to [###LICENSE_NAME###]. // Please see [###LICENSE_LINK###] for details. // All other rights reserved. using System; using System.Diagnostics; using System.Globalization; using System.Windows; using System.Windows.Markup; namespace Microsoft.Windows.Controls { // NOTICE: This date time helper assumes it is working in a Gregorian calendar // If we ever support non Gregorian calendars this class would need to be redesigned internal static class DateTimeHelper { private static System.Globalization.Calendar cal = new GregorianCalendar(); public static DateTime? AddDays(DateTime time, int days) { try { return cal.AddDays(time, days); } catch (System.ArgumentException) { return null; } } public static DateTime? AddMonths(DateTime time, int months) { try { return cal.AddMonths(time, months); } catch (System.ArgumentException) { return null; } } public static DateTime? AddYears(DateTime time, int years) { try { return cal.AddYears(time, years); } catch (System.ArgumentException) { return null; } } public static DateTime? SetYear(DateTime date, int year) { return DateTimeHelper.AddYears(date, year - date.Year); } public static DateTime? SetYearMonth(DateTime date, DateTime yearMonth) { DateTime? target = SetYear(date, yearMonth.Year); if (target.HasValue) { target = DateTimeHelper.AddMonths(target.Value, yearMonth.Month - date.Month); } return target; } public static int CompareDays(DateTime dt1, DateTime dt2) { return DateTime.Compare(DiscardTime(dt1).Value, DiscardTime(dt2).Value); } public static int CompareYearMonth(DateTime dt1, DateTime dt2) { return ((dt1.Year - dt2.Year) * 12) + (dt1.Month - dt2.Month); } public static int DecadeOfDate(DateTime date) { return date.Year - (date.Year % 10); } public static DateTime DiscardDayTime(DateTime d) { return new DateTime(d.Year, d.Month, 1, 0, 0, 0); } public static DateTime? DiscardTime(DateTime? d) { if (d == null) { return null; } return d.Value.Date; } public static int EndOfDecade(DateTime date) { return DecadeOfDate(date) + 9; } public static DateTimeFormatInfo GetCurrentDateFormat() { return GetDateFormat(CultureInfo.CurrentCulture); } internal static CultureInfo GetCulture(FrameworkElement element) { CultureInfo culture; if (DependencyPropertyHelper.GetValueSource(element, FrameworkElement.LanguageProperty).BaseValueSource != BaseValueSource.Default) { culture = GetCultureInfo(element); } else { culture = CultureInfo.CurrentCulture; } return culture; } // ------------------------------------------------------------------ // Retrieve CultureInfo property from specified element. // ------------------------------------------------------------------ internal static CultureInfo GetCultureInfo(DependencyObject element) { XmlLanguage language = (XmlLanguage)element.GetValue(FrameworkElement.LanguageProperty); try { return language.GetSpecificCulture(); } catch (InvalidOperationException) { // We default to en-US if no part of the language tag is recognized. return CultureInfo.ReadOnly(new CultureInfo("en-us", false)); } } internal static DateTimeFormatInfo GetDateFormat(CultureInfo culture) { if (culture.Calendar is GregorianCalendar) { return culture.DateTimeFormat; } else { GregorianCalendar foundCal = null; DateTimeFormatInfo dtfi = null; foreach (System.Globalization.Calendar cal in culture.OptionalCalendars) { if (cal is GregorianCalendar) { // Return the first Gregorian calendar with CalendarType == Localized // Otherwise return the first Gregorian calendar if (foundCal == null) { foundCal = cal as GregorianCalendar; } if (((GregorianCalendar)cal).CalendarType == GregorianCalendarTypes.Localized) { foundCal = cal as GregorianCalendar; break; } } } if (foundCal == null) { // if there are no GregorianCalendars in the OptionalCalendars list, use the invariant dtfi dtfi = ((CultureInfo)CultureInfo.InvariantCulture.Clone()).DateTimeFormat; dtfi.Calendar = new GregorianCalendar(); } else { dtfi = ((CultureInfo)culture.Clone()).DateTimeFormat; dtfi.Calendar = foundCal; } return dtfi; } } // returns if the date is included in the range public static bool InRange(DateTime date, CalendarDateRange range) { return InRange(date, range.Start, range.End); } // returns if the date is included in the range public static bool InRange(DateTime date, DateTime start, DateTime end) { Debug.Assert(DateTime.Compare(start, end) < 1); if (CompareDays(date, start) > -1 && CompareDays(date, end) < 1) { return true; } return false; } public static string ToDayString(DateTime? date, CultureInfo culture) { string result = string.Empty; DateTimeFormatInfo format = GetDateFormat(culture); if (date.HasValue && format != null) { result = date.Value.Day.ToString(format); } return result; } public static string ToDecadeRangeString(int decade, CultureInfo culture) { string result = string.Empty; DateTimeFormatInfo format = culture.DateTimeFormat; if (format != null) { int decadeEnd = decade + 9; result = decade.ToString(format) + "-" + decadeEnd.ToString(format); } return result; } public static string ToYearMonthPatternString(DateTime? date, CultureInfo culture) { string result = string.Empty; DateTimeFormatInfo format = GetDateFormat(culture); if (date.HasValue && format != null) { result = date.Value.ToString(format.YearMonthPattern, format); } return result; } public static string ToYearString(DateTime? date, CultureInfo culture) { string result = string.Empty; DateTimeFormatInfo format = GetDateFormat(culture); if (date.HasValue && format != null) { result = date.Value.Year.ToString(format); } return result; } public static string ToAbbreviatedMonthString(DateTime? date, CultureInfo culture) { string result = string.Empty; DateTimeFormatInfo format = GetDateFormat(culture); if (date.HasValue && format != null) { string[] monthNames = format.AbbreviatedMonthNames; if (monthNames != null && monthNames.Length > 0) { result = monthNames[(date.Value.Month - 1) % monthNames.Length]; } } return result; } public static string ToLongDateString(DateTime? date, CultureInfo culture) { string result = string.Empty; DateTimeFormatInfo format = GetDateFormat(culture); if (date.HasValue && format != null) { result = date.Value.Date.ToString(format.LongDatePattern, format); } return result; } } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/KeyboardHelper.cs ================================================ using System.Windows.Input; namespace Microsoft.Windows.Controls { internal static class KeyboardHelper { public static void GetMetaKeyState(out bool ctrl, out bool shift) { ctrl = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control; shift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift; } } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/SelectedDatesCollection.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Threading; using System.Windows; using System.Windows.Controls; namespace Microsoft.Windows.Controls { /// /// Represents the collection of SelectedDates for the Calendar Control. /// public sealed class SelectedDatesCollection : ObservableCollection { #region Data private Collection _addedItems; private Collection _removedItems; private Thread _dispatcherThread; private bool _isAddingRange; private Calendar _owner; private DateTime? _maximumDate; private DateTime? _minimumDate; #endregion Data /// /// Initializes a new instance of the CalendarSelectedDatesCollection class. /// /// public SelectedDatesCollection(Calendar owner) { this._dispatcherThread = Thread.CurrentThread; this._owner = owner; this._addedItems = new Collection(); this._removedItems = new Collection(); } #region Internal Properties internal DateTime? MinimumDate { get { if (Count < 1) { return null; } if (!_minimumDate.HasValue) { DateTime result = this[0]; foreach (DateTime selectedDate in this) { if (DateTime.Compare(selectedDate, result) < 0) { result = selectedDate; } } _maximumDate = result; } return _minimumDate; } } internal DateTime? MaximumDate { get { if (Count < 1) { return null; } if (!_maximumDate.HasValue) { DateTime result = this[0]; foreach (DateTime selectedDate in this) { if (DateTime.Compare(selectedDate, result) > 0) { result = selectedDate; } } _maximumDate = result; } return _maximumDate; } } #endregion #region Public methods /// /// Adds a range of dates to the Calendar SelectedDates. /// /// /// public void AddRange(DateTime start, DateTime end) { BeginAddRange(); // If CalendarSelectionMode.SingleRange and a user programmatically tries to add multiple ranges, we will throw away the old range and replace it with the new one. if (this._owner.SelectionMode == CalendarSelectionMode.SingleRange && this.Count > 0) { this.ClearInternal(); } foreach (DateTime current in GetDaysInRange(start, end)) { this.Add(current); } EndAddRange(); } #endregion Public Methods #region Protected methods /// /// Clears all the items of the SelectedDates. /// protected override void ClearItems() { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } // Turn off highlight this._owner.HoverStart = null; ClearInternal(true /*fireChangeNotification*/); } /// /// Inserts the item in the specified position of the SelectedDates collection. /// /// /// protected override void InsertItem(int index, DateTime item) { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } if (!this.Contains(item)) { Collection addedItems = new Collection(); bool isCleared = CheckSelectionMode(); if (Calendar.IsValidDateSelection(this._owner, item)) { // If the Collection is cleared since it is SingleRange and it had another range // set the index to 0 if (isCleared) { index = 0; isCleared = false; } base.InsertItem(index, item); UpdateMinMax(item); // The event fires after SelectedDate changes if (index == 0 && !(this._owner.SelectedDate.HasValue && DateTime.Compare(this._owner.SelectedDate.Value, item) == 0)) { this._owner.SelectedDate = item; } if (!_isAddingRange) { addedItems.Add(item); RaiseSelectionChanged(this._removedItems, addedItems); this._removedItems.Clear(); int monthDifference = DateTimeHelper.CompareYearMonth(item, this._owner.DisplayDateInternal); if (monthDifference < 2 && monthDifference > -2) { this._owner.UpdateCellItems(); } } else { this._addedItems.Add(item); } } else { throw new ArgumentOutOfRangeException(SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue)); } } } /// /// Removes the item at the specified position. /// /// protected override void RemoveItem(int index) { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } if (index >= this.Count) { base.RemoveItem(index); ClearMinMax(); } else { Collection addedItems = new Collection(); Collection removedItems = new Collection(); int monthDifference = DateTimeHelper.CompareYearMonth(this[index], this._owner.DisplayDateInternal); removedItems.Add(this[index]); base.RemoveItem(index); ClearMinMax(); // The event fires after SelectedDate changes if (index == 0) { if (Count > 0) { this._owner.SelectedDate = this[0]; } else { this._owner.SelectedDate = null; } } RaiseSelectionChanged(removedItems, addedItems); if (monthDifference < 2 && monthDifference > -2) { this._owner.UpdateCellItems(); } } } /// /// The object in the specified index is replaced with the provided item. /// /// /// protected override void SetItem(int index, DateTime item) { if (!IsValidThread()) { throw new NotSupportedException(SR.Get(SRID.CalendarCollection_MultiThreadedCollectionChangeNotSupported)); } if (!this.Contains(item)) { Collection addedItems = new Collection(); Collection removedItems = new Collection(); if (index >= this.Count) { base.SetItem(index, item); UpdateMinMax(item); } else { if (item != null && DateTime.Compare(this[index], item) != 0 && Calendar.IsValidDateSelection(this._owner, item)) { removedItems.Add(this[index]); base.SetItem(index, item); UpdateMinMax(item); addedItems.Add(item); // The event fires after SelectedDate changes if (index == 0 && !(this._owner.SelectedDate.HasValue && DateTime.Compare(this._owner.SelectedDate.Value, item) == 0)) { this._owner.SelectedDate = item; } RaiseSelectionChanged(removedItems, addedItems); int monthDifference = DateTimeHelper.CompareYearMonth(item, this._owner.DisplayDateInternal); if (monthDifference < 2 && monthDifference > -2) { this._owner.UpdateCellItems(); } } } } } #endregion Protected methods #region Internal Methods /// /// Adds a range of dates to the Calendar SelectedDates. /// /// /// Helper version of AddRange for mouse drag selection. /// This version guarantees no exceptions will be thrown by removing blackout days from the range before adding to the collection /// internal void AddRangeInternal(DateTime start, DateTime end) { BeginAddRange(); // In Mouse Selection we allow the user to be able to add multiple ranges in one action in MultipleRange Mode // In SingleRange Mode, we only add the first selected range DateTime lastAddedDate = start; foreach (DateTime current in GetDaysInRange(start, end)) { if (Calendar.IsValidDateSelection(this._owner, current)) { this.Add(current); lastAddedDate = current; } else { if (this._owner.SelectionMode == CalendarSelectionMode.SingleRange) { this._owner.CurrentDate = lastAddedDate; break; } } } EndAddRange(); } internal void ClearInternal() { ClearInternal(false /*fireChangeNotification*/); } internal void ClearInternal(bool fireChangeNotification) { if (this.Count > 0) { foreach (DateTime item in this) { _removedItems.Add(item); } base.ClearItems(); ClearMinMax(); if (fireChangeNotification) { if (this._owner.SelectedDate != null) { this._owner.SelectedDate = null; } if (_removedItems.Count > 0) { Collection addedItems = new Collection(); RaiseSelectionChanged(_removedItems, addedItems); _removedItems.Clear(); } this._owner.UpdateCellItems(); } } } internal void Toggle(DateTime date) { if (Calendar.IsValidDateSelection(this._owner, date)) { switch (this._owner.SelectionMode) { case CalendarSelectionMode.SingleDate: { if (!this._owner.SelectedDate.HasValue || DateTimeHelper.CompareDays(this._owner.SelectedDate.Value, date) != 0) { this._owner.SelectedDate = date; } else { this._owner.SelectedDate = null; } break; } case CalendarSelectionMode.MultipleRange: { if (!Remove(date)) { Add(date); } break; } default: { Debug.Assert(false); break; } } } } #endregion Internal Methods #region Private Methods private void RaiseSelectionChanged(IList removedItems, IList addedItems) { this._owner.OnSelectedDatesCollectionChanged(new CalendarSelectionChangedEventArgs(Calendar.SelectedDatesChangedEvent, removedItems, addedItems)); } private void BeginAddRange() { Debug.Assert(!_isAddingRange); _isAddingRange = true; } private void EndAddRange() { Debug.Assert(_isAddingRange); _isAddingRange = false; RaiseSelectionChanged(this._removedItems, this._addedItems); this._removedItems.Clear(); this._addedItems.Clear(); this._owner.UpdateCellItems(); } private bool CheckSelectionMode() { if (this._owner.SelectionMode == CalendarSelectionMode.None) { throw new InvalidOperationException(SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidOperation)); } if (this._owner.SelectionMode == CalendarSelectionMode.SingleDate && this.Count > 0) { throw new InvalidOperationException(SR.Get(SRID.Calendar_CheckSelectionMode_InvalidOperation)); } // if user tries to add an item into the SelectedDates in SingleRange mode, we throw away the old range and replace it with the new one // in order to provide the removed items without an additional event, we are calling ClearInternal if (this._owner.SelectionMode == CalendarSelectionMode.SingleRange && !_isAddingRange && this.Count > 0) { this.ClearInternal(); return true; } else { return false; } } private bool IsValidThread() { return Thread.CurrentThread == this._dispatcherThread; } private void UpdateMinMax(DateTime date) { if ((!_maximumDate.HasValue) || (date > _maximumDate.Value)) { _maximumDate = date; } if ((!_minimumDate.HasValue) || (date < _minimumDate.Value)) { _minimumDate = date; } } private void ClearMinMax() { _maximumDate = null; _minimumDate = null; } private static IEnumerable GetDaysInRange(DateTime start, DateTime end) { // increment parameter specifies if the Days were selected in Descending order or Ascending order // based on this value, we add the days in the range either in Ascending order or in Descending order int increment = GetDirection(start, end); DateTime? rangeStart = start; do { yield return rangeStart.Value; rangeStart = DateTimeHelper.AddDays(rangeStart.Value, increment); } while (rangeStart.HasValue && DateTime.Compare(end, rangeStart.Value) != -increment); } private static int GetDirection(DateTime start, DateTime end) { return (DateTime.Compare(end, start) >= 0) ? 1 : -1; } #endregion Private Methods } } ================================================ FILE: WpfToolkit/Calendar/Microsoft/Windows/Controls/VisualStates.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Windows; using System.Windows.Controls; namespace Microsoft.Windows.Controls { /// /// Names and helpers for visual states in the controls. /// internal static class VisualStates { #region GroupCalendarButtonFocus /// /// Unfocused state for Calendar Buttons /// public const string StateCalendarButtonUnfocused = "CalendarButtonUnfocused"; /// /// Focused state for Calendar Buttons /// public const string StateCalendarButtonFocused = "CalendarButtonFocused"; /// /// CalendarButtons Focus state group /// public const string GroupCalendarButtonFocus = "CalendarButtonFocusStates"; #endregion GroupCalendarButtonFocus #region GroupCommon /// /// Normal state /// public const string StateNormal = "Normal"; /// /// MouseOver state /// public const string StateMouseOver = "MouseOver"; /// /// Pressed state /// public const string StatePressed = "Pressed"; /// /// Disabled state /// public const string StateDisabled = "Disabled"; /// /// Common state group /// public const string GroupCommon = "CommonStates"; #endregion GroupCommon #region GroupFocus /// /// Unfocused state /// public const string StateUnfocused = "Unfocused"; /// /// Focused state /// public const string StateFocused = "Focused"; /// /// Focus state group /// public const string GroupFocus = "FocusStates"; #endregion GroupFocus #region GroupSelection /// /// Selected state /// public const string StateSelected = "Selected"; /// /// Unselected state /// public const string StateUnselected = "Unselected"; /// /// Selection state group /// public const string GroupSelection = "SelectionStates"; #endregion GroupSelection #region GroupActive /// /// Active state /// public const string StateActive = "Active"; /// /// Inactive state /// public const string StateInactive = "Inactive"; /// /// Active state group /// public const string GroupActive = "ActiveStates"; #endregion GroupActive #region GroupValidation /// /// Valid state /// public const string StateValid = "Valid"; /// /// InvalidFocused state /// public const string StateInvalidFocused = "InvalidFocused"; /// /// InvalidUnfocused state /// public const string StateInvalidUnfocused = "InvalidUnfocused"; /// /// Validation state group /// public const string GroupValidation = "ValidationStates"; #endregion GroupValidation #region GroupWatermark /// /// Unwatermarked state /// public const string StateUnwatermarked = "Unwatermarked"; /// /// Watermarked state /// public const string StateWatermarked = "Watermarked"; /// /// Watermark state group /// public const string GroupWatermark = "WatermarkStates"; #endregion GroupWatermark /// /// Use VisualStateManager to change the visual state of the control. /// /// /// Control whose visual state is being changed. /// /// /// true to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// /// /// Ordered list of state names and fallback states to transition into. /// Only the first state to be found will be used. /// public static void GoToState(Control control, bool useTransitions, params string[] stateNames) { if (control == null) { throw new ArgumentNullException("control"); } if (stateNames == null) { return; } foreach (string name in stateNames) { if (VisualStateManager.GoToState(control, name, useTransitions)) { break; } } } } } ================================================ FILE: WpfToolkit/Calendar/Themes/Aero.NormalColor.xaml ================================================  Aero.NormalColor ================================================ FILE: WpfToolkit/Calendar/Themes/Classic.xaml ================================================  Classic ================================================ FILE: WpfToolkit/Calendar/Themes/Generic.xaml ================================================  ================================================ FILE: WpfToolkit/Calendar/Themes/Luna.HomeStead.xaml ================================================  Luna.HomeStead ================================================ FILE: WpfToolkit/Calendar/Themes/Luna.Metallic.xaml ================================================  Luna.Metallic ================================================ FILE: WpfToolkit/Calendar/Themes/Luna.NormalColor.xaml ================================================  Luna.NormalColor ================================================ FILE: WpfToolkit/Calendar/Themes/Royale.NormalColor.xaml ================================================  Royale.NormalColor ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/BindingEvaluator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Windows.Data; namespace System.Windows.Controls { /// /// A framework element that permits a binding to be evaluated in a new data /// context leaf node. /// /// The type of dynamic binding to return. internal partial class BindingEvaluator : FrameworkElement { /// /// Gets or sets the string value binding used by the control. /// private Binding _binding; #region public T Value /// /// Gets or sets the data item string value. /// public T Value { get { return (T)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } /// /// Identifies the Value dependency property. /// public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(T), typeof(BindingEvaluator), new PropertyMetadata(default(T))); #endregion public string Value /// /// Gets or sets the value binding. /// public Binding ValueBinding { get { return _binding; } set { _binding = value; SetBinding(ValueProperty, _binding); } } /// /// Initializes a new instance of the BindingEvaluator class. /// public BindingEvaluator() { } /// /// Initializes a new instance of the BindingEvaluator class, /// setting the initial binding to the provided parameter. /// /// The initial string value binding. public BindingEvaluator(Binding binding) { SetBinding(ValueProperty, binding); } /// /// Clears the data context so that the control does not keep a /// reference to the last-looked up item. /// public void ClearDataContext() { DataContext = null; } /// /// Updates the data context of the framework element and returns the /// updated binding value. /// /// The object to use as the data context. /// If set to true, this parameter will /// clear the data context immediately after retrieving the value. /// Returns the evaluated T value of the bound dependency /// property. public T GetDynamicValue(object o, bool clearDataContext) { DataContext = o; T value = Value; if (clearDataContext) { DataContext = null; } return value; } /// /// Updates the data context of the framework element and returns the /// updated binding value. /// /// The object to use as the data context. /// Returns the evaluated T value of the bound dependency /// property. public T GetDynamicValue(object o) { DataContext = o; return Value; } } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/Extensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Windows; using System.Windows.Media; namespace System.Windows.Controls.Extensions { /// /// This set of internal extension methods provide general solutions and /// utilities in a small enough number to not warrant a dedicated extension /// methods class. /// internal static partial class Extensions { /// /// Inverts a Matrix. The Invert functionality on the Matrix type is /// internal to the framework only. Since Matrix is a struct, an out /// parameter must be presented. /// /// The Matrix object. /// The matrix to return by an output /// parameter. /// Returns a value indicating whether the type was /// successfully inverted. If the determinant is 0.0, then it cannot /// be inverted and the original instance will remain untouched. public static bool Invert(this Matrix m, out Matrix outputMatrix) { double determinant = m.M11 * m.M22 - m.M12 * m.M21; if (determinant == 0.0) { outputMatrix = m; return false; } Matrix matCopy = m; m.M11 = matCopy.M22 / determinant; m.M12 = -1 * matCopy.M12 / determinant; m.M21 = -1 * matCopy.M21 / determinant; m.M22 = matCopy.M11 / determinant; m.OffsetX = (matCopy.OffsetY * matCopy.M21 - matCopy.OffsetX * matCopy.M22) / determinant; m.OffsetY = (matCopy.OffsetX * matCopy.M12 - matCopy.OffsetY * matCopy.M11) / determinant; outputMatrix = m; return true; } /// /// An implementation of the Contains member of string that takes in a /// string comparison. The traditional .NET string Contains member uses /// StringComparison.Ordinal. /// /// The string. /// The string value to search for. /// The string comparison type. /// Returns true when the substring is found. public static bool Contains(this string s, string value, StringComparison comparison) { return s.IndexOf(value, comparison) >= 0; } } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/IUpdateVisualState.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls { /// /// The IUpdateVisualState interface is used to provide the /// InteractionHelper with access to the type's UpdateVisualState method. /// [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This is not an exception class.")] internal interface IUpdateVisualState { /// /// Update the visual state of the control. /// /// /// A value indicating whether to automatically generate transitions to /// the new state, or instantly transition to the new state. /// void UpdateVisualState(bool useTransitions); } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/InteractionHelper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace System.Windows.Controls { /// /// The InteractionHelper provides controls with support for all of the /// common interactions like mouse movement, mouse clicks, key presses, /// etc., and also incorporates proper event semantics when the control is /// disabled. /// internal sealed partial class InteractionHelper { // TODO: Consult with user experience experts to validate the double // click distance and time thresholds. /// /// The threshold used to determine whether two clicks are temporally /// local and considered a double click (or triple, quadruple, etc.). /// 500 milliseconds is the default double click value on Windows. /// This value would ideally be pulled form the system settings. /// private const double SequentialClickThresholdInMilliseconds = 500.0; /// /// The threshold used to determine whether two clicks are spatially /// local and considered a double click (or triple, quadruple, etc.) /// in pixels squared. We use pixels squared so that we can compare to /// the distance delta without taking a square root. /// private const double SequentialClickThresholdInPixelsSquared = 3.0 * 3.0; /// /// Gets the control the InteractionHelper is targeting. /// public Control Control { get; private set; } /// /// Gets a value indicating whether the control has focus. /// public bool IsFocused { get; private set; } /// /// Gets a value indicating whether the mouse is over the control. /// public bool IsMouseOver { get; private set; } /// /// Gets a value indicating whether the read-only property is set. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Linked file.")] public bool IsReadOnly { get; private set; } /// /// Gets a value indicating whether the mouse button is pressed down /// over the control. /// public bool IsPressed { get; private set; } /// /// Gets or sets the last time the control was clicked. /// /// /// The value is stored as Utc time because it is slightly more /// performant than converting to local time. /// private DateTime LastClickTime { get; set; } /// /// Gets or sets the mouse position of the last click. /// /// The value is relative to the control. private Point LastClickPosition { get; set; } /// /// Gets the number of times the control was clicked. /// public int ClickCount { get; private set; } /// /// Reference used to call UpdateVisualState on the base class. /// private IUpdateVisualState _updateVisualState; /// /// Initializes a new instance of the InteractionHelper class. /// /// Control receiving interaction. public InteractionHelper(Control control) { Debug.Assert(control != null, "control should not be null!"); Control = control; _updateVisualState = control as IUpdateVisualState; // Wire up the event handlers for events without a virtual override control.Loaded += OnLoaded; control.IsEnabledChanged += OnIsEnabledChanged; } #region UpdateVisualState /// /// Update the visual state of the control. /// /// /// A value indicating whether to automatically generate transitions to /// the new state, or instantly transition to the new state. /// /// /// UpdateVisualState works differently than the rest of the injected /// functionality. Most of the other events are overridden by the /// calling class which calls Allow, does what it wants, and then calls /// Base. UpdateVisualState is the opposite because a number of the /// methods in InteractionHelper need to trigger it in the calling /// class. We do this using the IUpdateVisualState internal interface. /// private void UpdateVisualState(bool useTransitions) { if (_updateVisualState != null) { _updateVisualState.UpdateVisualState(useTransitions); } } /// /// Update the visual state of the control. /// /// /// A value indicating whether to automatically generate transitions to /// the new state, or instantly transition to the new state. /// public void UpdateVisualStateBase(bool useTransitions) { // Handle the Common states if (!Control.IsEnabled) { VisualStates.GoToState(Control, useTransitions, VisualStates.StateDisabled, VisualStates.StateNormal); } else if (IsReadOnly) { VisualStates.GoToState(Control, useTransitions, VisualStates.StateReadOnly, VisualStates.StateNormal); } else if (IsPressed) { VisualStates.GoToState(Control, useTransitions, VisualStates.StatePressed, VisualStates.StateMouseOver, VisualStates.StateNormal); } else if (IsMouseOver) { VisualStates.GoToState(Control, useTransitions, VisualStates.StateMouseOver, VisualStates.StateNormal); } else { VisualStates.GoToState(Control, useTransitions, VisualStates.StateNormal); } // Handle the Focused states if (IsFocused) { VisualStates.GoToState(Control, useTransitions, VisualStates.StateFocused, VisualStates.StateUnfocused); } else { VisualStates.GoToState(Control, useTransitions, VisualStates.StateUnfocused); } } #endregion UpdateVisualState /// /// Handle the control's Loaded event. /// /// The control. /// Event arguments. private void OnLoaded(object sender, RoutedEventArgs e) { UpdateVisualState(false); } /// /// Handle changes to the control's IsEnabled property. /// /// The control. /// Event arguments. private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { bool enabled = (bool) e.NewValue; if (!enabled) { IsPressed = false; IsMouseOver = false; IsFocused = false; } UpdateVisualState(true); } /// /// Handles changes to the control's IsReadOnly property. /// /// The value of the property. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Linked file.")] public void OnIsReadOnlyChanged(bool value) { IsReadOnly = value; if (!value) { IsPressed = false; IsMouseOver = false; IsFocused = false; } UpdateVisualState(true); } /// /// Update the visual state of the control when its template is changed. /// public void OnApplyTemplateBase() { UpdateVisualState(false); } #region GotFocus /// /// Check if the control's GotFocus event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowGotFocus(RoutedEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } bool enabled = Control.IsEnabled; if (enabled) { IsFocused = true; } return enabled; } /// /// Base implementation of the virtual GotFocus event handler. /// public void OnGotFocusBase() { UpdateVisualState(true); } #endregion GotFocus #region LostFocus /// /// Check if the control's LostFocus event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowLostFocus(RoutedEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } bool enabled = Control.IsEnabled; if (enabled) { IsFocused = false; } return enabled; } /// /// Base implementation of the virtual LostFocus event handler. /// public void OnLostFocusBase() { IsPressed = false; UpdateVisualState(true); } #endregion LostFocus #region MouseEnter /// /// Check if the control's MouseEnter event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowMouseEnter(MouseEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } bool enabled = Control.IsEnabled; if (enabled) { IsMouseOver = true; } return enabled; } /// /// Base implementation of the virtual MouseEnter event handler. /// public void OnMouseEnterBase() { UpdateVisualState(true); } #endregion MouseEnter #region MouseLeave /// /// Check if the control's MouseLeave event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowMouseLeave(MouseEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } bool enabled = Control.IsEnabled; if (enabled) { IsMouseOver = false; } return enabled; } /// /// Base implementation of the virtual MouseLeave event handler. /// public void OnMouseLeaveBase() { UpdateVisualState(true); } #endregion MouseLeave #region MouseLeftButtonDown /// /// Check if the control's MouseLeftButtonDown event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowMouseLeftButtonDown(MouseButtonEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } bool enabled = Control.IsEnabled; if (enabled) { // Get the current position and time DateTime now = DateTime.UtcNow; Point position = e.GetPosition(Control); // Compute the deltas from the last click double timeDelta = (now - LastClickTime).TotalMilliseconds; Point lastPosition = LastClickPosition; double dx = position.X - lastPosition.X; double dy = position.Y - lastPosition.Y; double distance = dx * dx + dy * dy; // Check if the values fall under the sequential click temporal // and spatial thresholds if (timeDelta < SequentialClickThresholdInMilliseconds && distance < SequentialClickThresholdInPixelsSquared) { // TODO: Does each click have to be within the single time // threshold on WPF? ClickCount++; } else { ClickCount = 1; } // Set the new position and time LastClickTime = now; LastClickPosition = position; // Raise the event IsPressed = true; } else { ClickCount = 1; } return enabled; } /// /// Base implementation of the virtual MouseLeftButtonDown event /// handler. /// public void OnMouseLeftButtonDownBase() { UpdateVisualState(true); } #endregion MouseLeftButtonDown #region MouseLeftButtonUp /// /// Check if the control's MouseLeftButtonUp event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowMouseLeftButtonUp(MouseButtonEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } bool enabled = Control.IsEnabled; if (enabled) { IsPressed = false; } return enabled; } /// /// Base implementation of the virtual MouseLeftButtonUp event handler. /// public void OnMouseLeftButtonUpBase() { UpdateVisualState(true); } #endregion MouseLeftButtonUp #region KeyDown /// /// Check if the control's KeyDown event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowKeyDown(KeyEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } return Control.IsEnabled; } #endregion KeyDown #region KeyUp /// /// Check if the control's KeyUp event should be handled. /// /// Event arguments. /// /// A value indicating whether the event should be handled. /// public bool AllowKeyUp(KeyEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } return Control.IsEnabled; } #endregion KeyUp } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/ItemsControlHelper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; namespace System.Windows.Controls { /// /// The ItemContainerGenerator provides useful utilities for ItemsControls. /// /// Preview internal sealed partial class ItemsControlHelper { /// /// Gets or sets the ItemsControl being tracked by the /// ItemContainerGenerator. /// private ItemsControl ItemsControl { get; set; } /// /// A Panel that is used as the ItemsHost of the ItemsControl. This /// property will only be valid when the ItemsControl is live in the /// tree and has generated containers for some of its items. /// private Panel _itemsHost; /// /// Gets a Panel that is used as the ItemsHost of the ItemsControl. /// This property will only be valid when the ItemsControl is live in /// the tree and has generated containers for some of its items. /// internal Panel ItemsHost { get { // Lookup the ItemsHost if we haven't already cached it. if (_itemsHost == null && ItemsControl != null && ItemsControl.ItemContainerGenerator != null) { // Get any live container DependencyObject container = ItemsControl.ItemContainerGenerator.ContainerFromIndex(0); if (container != null) { // Get the parent of the container _itemsHost = VisualTreeHelper.GetParent(container) as Panel; } } return _itemsHost; } } /// /// A ScrollViewer that is used to scroll the items in the ItemsHost. /// private ScrollViewer _scrollHost; /// /// Gets a ScrollViewer that is used to scroll the items in the /// ItemsHost. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Code is linked into multiple projects.")] internal ScrollViewer ScrollHost { get { if (_scrollHost == null) { Panel itemsHost = ItemsHost; if (itemsHost != null) { for (DependencyObject obj = itemsHost; obj != ItemsControl && obj != null; obj = VisualTreeHelper.GetParent(obj)) { ScrollViewer viewer = obj as ScrollViewer; if (viewer != null) { _scrollHost = viewer; break; } } } } return _scrollHost; } } /// /// Initializes a new instance of the ItemContainerGenerator. /// /// /// The ItemsControl being tracked by the ItemContainerGenerator. /// internal ItemsControlHelper(ItemsControl control) { Debug.Assert(control != null, "control cannot be null!"); ItemsControl = control; } /// /// Apply a control template to the ItemsControl. /// internal void OnApplyTemplate() { // Clear the cached ItemsHost, ScrollHost _itemsHost = null; _scrollHost = null; } /// /// Prepares the specified container to display the specified item. /// /// /// Container element used to display the specified item. /// /// /// The ItemContainerStyle for the parent ItemsControl. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Code is linked into multiple projects.")] internal static void PrepareContainerForItemOverride(DependencyObject element, Style parentItemContainerStyle) { // Apply the ItemContainerStyle to the item Control control = element as Control; if (parentItemContainerStyle != null && control != null && control.Style == null) { control.SetValue(Control.StyleProperty, parentItemContainerStyle); } // Note: WPF also does preparation for ContentPresenter, // ContentControl, HeaderedContentControl, and ItemsControl. Since // we don't have any other ItemsControls using this // ItemContainerGenerator, we've removed that code for now. It // should be added back later when necessary. } /// /// Update the style of any generated items when the ItemContainerStyle /// has been changed. /// /// The ItemContainerStyle. internal void UpdateItemContainerStyle(Style itemContainerStyle) { if (itemContainerStyle == null) { return; } Panel itemsHost = ItemsHost; if (itemsHost == null || itemsHost.Children == null) { return; } foreach (UIElement element in itemsHost.Children) { FrameworkElement obj = element as FrameworkElement; if (obj.Style == null) { obj.Style = itemContainerStyle; } } } /// /// Scroll the desired element into the ScrollHost's viewport. /// /// Element to scroll into view. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "File is linked across multiple projects and this method is used in some but not others.")] internal void ScrollIntoView(FrameworkElement element) { // Get the ScrollHost ScrollViewer scrollHost = ScrollHost; if (scrollHost == null) { return; } // Get the position of the element relative to the ScrollHost GeneralTransform transform = null; try { transform = element.TransformToVisual(scrollHost); } catch (ArgumentException) { // Ignore failures when not in the visual tree return; } Rect itemRect = new Rect( transform.Transform(new Point()), transform.Transform(new Point(element.ActualWidth, element.ActualHeight))); // Scroll vertically double verticalOffset = scrollHost.VerticalOffset; double verticalDelta = 0; double hostBottom = scrollHost.ViewportHeight; double itemBottom = itemRect.Bottom; if (hostBottom < itemBottom) { verticalDelta = itemBottom - hostBottom; verticalOffset += verticalDelta; } double itemTop = itemRect.Top; if (itemTop - verticalDelta < 0) { verticalOffset -= verticalDelta - itemTop; } scrollHost.ScrollToVerticalOffset(verticalOffset); // Scroll horizontally double horizontalOffset = scrollHost.HorizontalOffset; double horizontalDelta = 0; double hostRight = scrollHost.ViewportWidth; double itemRight = itemRect.Right; if (hostRight < itemRight) { horizontalDelta = itemRight - hostRight; horizontalOffset += horizontalDelta; } double itemLeft = itemRect.Left; if (itemLeft - horizontalDelta < 0) { horizontalOffset -= horizontalDelta - itemLeft; } scrollHost.ScrollToHorizontalOffset(horizontalOffset); } } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/VisualStates.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace System.Windows.Controls { /// /// Names and helpers for visual states in the controls. /// internal static class VisualStates { #region GroupCommon /// /// Common state group. /// public const string GroupCommon = "CommonStates"; /// /// Normal state of the Common state group. /// public const string StateNormal = "Normal"; /// /// Normal state of the Common state group. /// public const string StateReadOnly = "ReadOnly"; /// /// MouseOver state of the Common state group. /// public const string StateMouseOver = "MouseOver"; /// /// Pressed state of the Common state group. /// public const string StatePressed = "Pressed"; /// /// Disabled state of the Common state group. /// public const string StateDisabled = "Disabled"; #endregion GroupCommon #region GroupFocus /// /// Focus state group. /// public const string GroupFocus = "FocusStates"; /// /// Unfocused state of the Focus state group. /// public const string StateUnfocused = "Unfocused"; /// /// Focused state of the Focus state group. /// public const string StateFocused = "Focused"; #endregion GroupFocus #region GroupSelection /// /// Selection state group. /// public const string GroupSelection = "SelectionStates"; /// /// Selected state of the Selection state group. /// public const string StateSelected = "Selected"; /// /// Unselected state of the Selection state group. /// public const string StateUnselected = "Unselected"; /// /// Selected inactive state of the Selection state group. /// public const string StateSelectedInactive = "SelectedInactive"; #endregion GroupSelection #region GroupExpansion /// /// Expansion state group. /// public const string GroupExpansion = "ExpansionStates"; /// /// Expanded state of the Expansion state group. /// public const string StateExpanded = "Expanded"; /// /// Collapsed state of the Expansion state group. /// public const string StateCollapsed = "Collapsed"; #endregion GroupExpansion #region GroupPopup /// /// Popup state group. /// public const string GroupPopup = "PopupStates"; /// /// Opened state of the Popup state group. /// public const string StatePopupOpened = "PopupOpened"; /// /// Closed state of the Popup state group. /// public const string StatePopupClosed = "PopupClosed"; #endregion #region GroupValidation /// /// ValidationStates state group. /// public const string GroupValidation = "ValidationStates"; /// /// The valid state for the ValidationStates group. /// public const string StateValid = "Valid"; /// /// Invalid, focused state for the ValidationStates group. /// public const string StateInvalidFocused = "InvalidFocused"; /// /// Invalid, unfocused state for the ValidationStates group. /// public const string StateInvalidUnfocused = "InvalidUnfocused"; #endregion #region GroupExpandDirection /// /// ExpandDirection state group. /// public const string GroupExpandDirection = "ExpandDirectionStates"; /// /// Down expand direction state of ExpandDirection state group. /// public const string StateExpandDown = "ExpandDown"; /// /// Up expand direction state of ExpandDirection state group. /// public const string StateExpandUp = "ExpandUp"; /// /// Left expand direction state of ExpandDirection state group. /// public const string StateExpandLeft = "ExpandLeft"; /// /// Right expand direction state of ExpandDirection state group. /// public const string StateExpandRight = "ExpandRight"; #endregion #region GroupHasItems /// /// HasItems state group. /// public const string GroupHasItems = "HasItemsStates"; /// /// HasItems state of the HasItems state group. /// public const string StateHasItems = "HasItems"; /// /// NoItems state of the HasItems state group. /// public const string StateNoItems = "NoItems"; #endregion GroupHasItems #region GroupIncrease /// /// Increment state group. /// public const string GroupIncrease = "IncreaseStates"; /// /// State enabled for increment group. /// public const string StateIncreaseEnabled = "IncreaseEnabled"; /// /// State disabled for increment group. /// public const string StateIncreaseDisabled = "IncreaseDisabled"; #endregion GroupIncrease #region GroupDecrease /// /// Decrement state group. /// public const string GroupDecrease = "DecreaseStates"; /// /// State enabled for decrement group. /// public const string StateDecreaseEnabled = "DecreaseEnabled"; /// /// State disabled for decrement group. /// public const string StateDecreaseDisabled = "DecreaseDisabled"; #endregion GroupDecrease #region GroupIteractionMode /// /// InteractionMode state group. /// public const string GroupInteractionMode = "InteractionModeStates"; /// /// Edit of the DisplayMode state group. /// public const string StateEdit = "Edit"; /// /// Display of the DisplayMode state group. /// public const string StateDisplay = "Display"; #endregion GroupIteractionMode #region GroupLocked /// /// DisplayMode state group. /// public const string GroupLocked = "LockedStates"; /// /// Edit of the DisplayMode state group. /// public const string StateLocked = "Locked"; /// /// Display of the DisplayMode state group. /// public const string StateUnlocked = "Unlocked"; #endregion GroupLocked #region GroupActive /// /// Active state. /// public const string StateActive = "Active"; /// /// Inactive state. /// public const string StateInactive = "Inactive"; /// /// Active state group. /// public const string GroupActive = "ActiveStates"; #endregion GroupActive #region GroupWatermark /// /// Non-watermarked state. /// public const string StateUnwatermarked = "Unwatermarked"; /// /// Watermarked state. /// public const string StateWatermarked = "Watermarked"; /// /// Watermark state group. /// public const string GroupWatermark = "WatermarkStates"; #endregion GroupWatermark #region GroupCalendarButtonFocus /// /// Unfocused state for Calendar Buttons. /// public const string StateCalendarButtonUnfocused = "CalendarButtonUnfocused"; /// /// Focused state for Calendar Buttons. /// public const string StateCalendarButtonFocused = "CalendarButtonFocused"; /// /// CalendarButtons Focus state group. /// public const string GroupCalendarButtonFocus = "CalendarButtonFocusStates"; #endregion GroupCalendarButtonFocus #region GroupBusyStatus /// /// Busy state for BusyIndicator. /// public const string StateBusy = "Busy"; /// /// Idle state for BusyIndicator. /// public const string StateIdle = "Idle"; /// /// Busyness group name. /// public const string GroupBusyStatus = "BusyStatusStates"; #endregion #region GroupVisibility /// /// Visible state name for BusyIndicator. /// public const string StateVisible = "Visible"; /// /// Hidden state name for BusyIndicator. /// public const string StateHidden = "Hidden"; /// /// BusyDisplay group. /// public const string GroupVisibility = "VisibilityStates"; #endregion /// /// Use VisualStateManager to change the visual state of the control. /// /// /// Control whose visual state is being changed. /// /// /// A value indicating whether to use transitions when updating the /// visual state, or to snap directly to the new visual state. /// /// /// Ordered list of state names and fallback states to transition into. /// Only the first state to be found will be used. /// public static void GoToState(Control control, bool useTransitions, params string[] stateNames) { Debug.Assert(control != null, "control should not be null!"); Debug.Assert(stateNames != null, "stateNames should not be null!"); Debug.Assert(stateNames.Length > 0, "stateNames should not be empty!"); foreach (string name in stateNames) { if (VisualStateManager.GoToState(control, name, useTransitions)) { break; } } } /// /// Gets the implementation root of the Control. /// /// The DependencyObject. /// Returns the implementation root or null. public static FrameworkElement GetImplementationRoot(DependencyObject dependencyObject) { Debug.Assert(dependencyObject != null, "DependencyObject should not be null."); return (1 == VisualTreeHelper.GetChildrenCount(dependencyObject)) ? VisualTreeHelper.GetChild(dependencyObject, 0) as FrameworkElement : null; } /// /// This method tries to get the named VisualStateGroup for the /// dependency object. The provided object's ImplementationRoot will be /// looked up in this call. /// /// The dependency object. /// The visual state group's name. /// Returns null or the VisualStateGroup object. public static VisualStateGroup TryGetVisualStateGroup(DependencyObject dependencyObject, string groupName) { FrameworkElement root = GetImplementationRoot(dependencyObject); if (root == null) { return null; } return VisualStateManager.GetVisualStateGroups(root) .OfType() .Where(group => string.CompareOrdinal(groupName, group.Name) == 0) .FirstOrDefault(); } } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/VisualTreeExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Media; namespace System.Windows.Controls { /// /// A static class providing methods for working with the visual tree. /// internal static class VisualTreeExtensions { /// /// Retrieves all the visual children of a framework element. /// /// The parent framework element. /// The visual children of the framework element. internal static IEnumerable GetVisualChildren(this DependencyObject parent) { Debug.Assert(parent != null, "The parent cannot be null."); int childCount = VisualTreeHelper.GetChildrenCount(parent); for (int counter = 0; counter < childCount; counter++) { yield return VisualTreeHelper.GetChild(parent, counter); } } /// /// Retrieves all the logical children of a framework element using a /// breadth-first search. A visual element is assumed to be a logical /// child of another visual element if they are in the same namescope. /// For performance reasons this method manually manages the queue /// instead of using recursion. /// /// The parent framework element. /// The logical children of the framework element. internal static IEnumerable GetLogicalChildrenBreadthFirst(this FrameworkElement parent) { Debug.Assert(parent != null, "The parent cannot be null."); Queue queue = new Queue(parent.GetVisualChildren().OfType()); while (queue.Count > 0) { FrameworkElement element = queue.Dequeue(); yield return element; foreach (FrameworkElement visualChild in element.GetVisualChildren().OfType()) { queue.Enqueue(visualChild); } } } } } ================================================ FILE: WpfToolkit/Common/System/Windows/Controls/WeakEventListener.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls { /// /// Implements a weak event listener that allows the owner to be garbage /// collected if its only remaining link is an event handler. /// /// Type of instance listening for the event. /// Type of source for the event. /// Type of event arguments for the event. [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Used as link target in several projects.")] internal class WeakEventListener where TInstance : class { /// /// WeakReference to the instance listening for the event. /// private WeakReference _weakInstance; /// /// Gets or sets the method to call when the event fires. /// public Action OnEventAction { get; set; } /// /// Gets or sets the method to call when detaching from the event. /// public Action> OnDetachAction { get; set; } /// /// Initializes a new instances of the WeakEventListener class. /// /// Instance subscribing to the event. public WeakEventListener(TInstance instance) { if (null == instance) { throw new ArgumentNullException("instance"); } _weakInstance = new WeakReference(instance); } /// /// Handler for the subscribed event calls OnEventAction to handle it. /// /// Event source. /// Event arguments. public void OnEvent(TSource source, TEventArgs eventArgs) { TInstance target = (TInstance)_weakInstance.Target; if (null != target) { // Call registered action if (null != OnEventAction) { OnEventAction(target, source, eventArgs); } } else { // Detach from event Detach(); } } /// /// Detaches from the subscribed event. /// public void Detach() { if (null != OnDetachAction) { OnDetachAction(this); OnDetachAction = null; } } } } ================================================ FILE: WpfToolkit/DataVisualization/AggregatedObservableCollection.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; namespace System.Windows.Controls.DataVisualization { /// /// Aggregated observable collection. /// /// The type of the items in the observable collections. /// internal class AggregatedObservableCollection : ReadOnlyObservableCollection { /// /// Initializes a new instance of an aggregated observable collection. /// public AggregatedObservableCollection() { this.ChildCollections = new NoResetObservableCollection(); this.ChildCollections.CollectionChanged += new NotifyCollectionChangedEventHandler(ChildCollectionsCollectionChanged); } /// /// Rebuilds the list if a collection changes. /// /// The source of the event. /// Information about the event. private void ChildCollectionsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Debug.Assert(e.Action != NotifyCollectionChangedAction.Reset, "Reset is not supported."); if (e.Action == NotifyCollectionChangedAction.Add) { e.NewItems .OfType() .ForEachWithIndex((newCollection, index) => { int startingIndex = GetStartingIndexOfCollectionAtIndex(e.NewStartingIndex + index); foreach (T item in newCollection.OfType().Reverse()) { this.Mutate(items => items.Insert(startingIndex, item)); } INotifyCollectionChanged notifyCollectionChanged = newCollection as INotifyCollectionChanged; if (notifyCollectionChanged != null) { notifyCollectionChanged.CollectionChanged += ChildCollectionCollectionChanged; } }); } else if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (IList oldCollection in e.OldItems) { INotifyCollectionChanged notifyCollectionChanged = oldCollection as INotifyCollectionChanged; if (notifyCollectionChanged != null) { notifyCollectionChanged.CollectionChanged -= ChildCollectionCollectionChanged; } foreach (T item in oldCollection) { this.Mutate(items => items.Remove(item)); } } } else if (e.Action == NotifyCollectionChangedAction.Replace) { foreach (IList oldCollection in e.OldItems) { INotifyCollectionChanged notifyCollectionChanged = oldCollection as INotifyCollectionChanged; if (notifyCollectionChanged != null) { notifyCollectionChanged.CollectionChanged -= ChildCollectionCollectionChanged; } } foreach (IList newCollection in e.NewItems) { INotifyCollectionChanged notifyCollectionChanged = newCollection as INotifyCollectionChanged; if (notifyCollectionChanged != null) { notifyCollectionChanged.CollectionChanged += ChildCollectionCollectionChanged; } } Rebuild(); } } /// /// Synchronizes the collection with changes made in a child collection. /// /// The source of the event. /// Information about the event. private void ChildCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { Debug.Assert(e.Action != NotifyCollectionChangedAction.Reset, "Reset is not supported."); IList collectionSender = sender as IList; if (e.Action == NotifyCollectionChangedAction.Add) { int startingIndex = GetStartingIndexOfCollectionAtIndex(ChildCollections.IndexOf(collectionSender)); e.NewItems .OfType() .ForEachWithIndex((item, index) => { this.Mutate(that => that.Insert(startingIndex + e.NewStartingIndex + index, item)); }); } else if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (T item in e.OldItems.OfType()) { this.Mutate(that => that.Remove(item)); } } else if (e.Action == NotifyCollectionChangedAction.Replace) { for (int cnt = 0; cnt < e.NewItems.Count; cnt++) { T oldItem = (T)e.OldItems[cnt]; T newItem = (T)e.NewItems[cnt]; int oldItemIndex = this.IndexOf(oldItem); this.Mutate((that) => { that[oldItemIndex] = newItem; }); } } } /// /// Returns the starting index of a collection in the aggregate /// collection. /// /// The starting index of a collection. /// The starting index of the collection in the aggregate /// collection. private int GetStartingIndexOfCollectionAtIndex(int index) { return ChildCollections.OfType().Select(collection => collection.CastWrapper()).Take(index).SelectMany(collection => collection).Count(); } /// /// Rebuild the list in the correct order when a child collection /// changes. /// private void Rebuild() { this.Mutate(that => that.Clear()); this.Mutate(that => { IList items = ChildCollections.OfType().Select(collection => collection.CastWrapper()).SelectMany(collection => collection).ToList(); foreach (T item in items) { that.Add(item); } }); } /// /// Gets child collections of the aggregated collection. /// public ObservableCollection ChildCollections { get; private set; } } } ================================================ FILE: WpfToolkit/DataVisualization/AssemblyInfoShared.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Resources; using System.Windows.Markup; // WPF shared settings [assembly: CLSCompliant(true)] [assembly: NeutralResourcesLanguage("en-US")] [assembly: XmlnsDefinition("clr-namespace:System.Windows.Controls.DataVisualization;assembly=DotNetProjects.DataVisualization.Toolkit", "System.Windows.Controls.DataVisualization")] [assembly: XmlnsDefinition("clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=DotNetProjects.DataVisualization.Toolkit", "System.Windows.Controls.DataVisualization.Charting")] [assembly: XmlnsPrefix("clr-namespace:System.Windows.Controls.DataVisualization;assembly=DotNetProjects.DataVisualization.Toolkit", "visualizationToolkit")] [assembly: XmlnsPrefix("clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=DotNetProjects.DataVisualization.Toolkit", "chartingToolkit")] #if !NO_XMLNSDEFINITION_URIS [assembly: XmlnsPrefix("http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit", "toolkit")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit", "System.Windows.Controls.DataVisualization")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit", "System.Windows.Controls.DataVisualization.Charting")] #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/AnimationSequence.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Specifies the supported animation sequences. /// /// Preview public enum AnimationSequence { /// /// Animates all of the data points simultaneously. /// Simultaneous = 0, /// /// Animates the data points from first to last. /// FirstToLast = 1, /// /// Animates the data points from last to first. /// LastToFirst = 2 } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/Axis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Controls; using System.Collections.ObjectModel; using System.Collections.Specialized; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis class used to determine the plot area coordinate of values. /// public abstract class Axis : Control, IAxis { #region public AxisLocation Location /// /// Gets or sets the axis location. /// public AxisLocation Location { get { return (AxisLocation)GetValue(LocationProperty); } set { SetValue(LocationProperty, value); } } /// /// Identifies the Location dependency property. /// public static readonly DependencyProperty LocationProperty = DependencyProperty.Register( "Location", typeof(AxisLocation), typeof(Axis), new PropertyMetadata(AxisLocation.Auto, OnLocationPropertyChanged)); /// /// LocationProperty property changed handler. /// /// Axis that changed its Location. /// Event arguments. private static void OnLocationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Axis source = (Axis)d; AxisLocation oldValue = (AxisLocation)e.OldValue; AxisLocation newValue = (AxisLocation)e.NewValue; source.OnLocationPropertyChanged(oldValue, newValue); } /// /// LocationProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnLocationPropertyChanged(AxisLocation oldValue, AxisLocation newValue) { RoutedPropertyChangedEventHandler handler = this.LocationChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } /// /// This event is raised when the location property is changed. /// public event RoutedPropertyChangedEventHandler LocationChanged; #endregion public AxisLocation Location /// /// Gets the list of child axes belonging to this axis. /// public ObservableCollection DependentAxes { get; private set; } #region public AxisOrientation Orientation /// /// Gets or sets the orientation of the axis. /// public AxisOrientation Orientation { get { return (AxisOrientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } /// /// Identifies the Orientation dependency property. /// public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( "Orientation", typeof(AxisOrientation), typeof(Axis), new PropertyMetadata(AxisOrientation.None, OnOrientationPropertyChanged)); /// /// OrientationProperty property changed handler. /// /// Axis that changed its Orientation. /// Event arguments. private static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Axis source = (Axis)d; AxisOrientation oldValue = (AxisOrientation)e.OldValue; AxisOrientation newValue = (AxisOrientation)e.NewValue; source.OnOrientationPropertyChanged(oldValue, newValue); } /// /// OrientationProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnOrientationPropertyChanged(AxisOrientation oldValue, AxisOrientation newValue) { RoutedPropertyChangedEventHandler handler = OrientationChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } /// /// This event is raised when the Orientation property is changed. /// public event RoutedPropertyChangedEventHandler OrientationChanged; #endregion public AxisOrientation Orientation /// /// Raises the invalidated event. /// /// Information about the event. protected virtual void OnInvalidated(RoutedEventArgs args) { foreach (IAxisListener listener in RegisteredListeners) { listener.AxisInvalidated(this); } } /// /// Gets or the collection of series that are using the Axis. /// public ObservableCollection RegisteredListeners { get; private set; } /// /// Returns a value indicating whether the axis can plot a value. /// /// The value to plot. /// A value indicating whether the axis can plot a value. /// public abstract bool CanPlot(object value); /// /// The plot area coordinate of a value. /// /// The value for which to retrieve the plot area /// coordinate. /// The plot area coordinate. public abstract UnitValue GetPlotAreaCoordinate(object value); /// /// Instantiates a new instance of the Axis class. /// protected Axis() { RegisteredListeners = new UniqueObservableCollection(); this.RegisteredListeners.CollectionChanged += RegisteredListenersCollectionChanged; this.DependentAxes = new ObservableCollection(); this.DependentAxes.CollectionChanged += OnChildAxesCollectionChanged; } /// /// Child axes collection changed. /// /// The source of the event. /// Information about the event. private void OnChildAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { this.OnDependentAxesCollectionChanged(); } /// /// Child axes collection changed. /// protected virtual void OnDependentAxesCollectionChanged() { } /// /// This event is raised when the registered listeners collection is /// changed. /// /// The source of the event. /// Information about the event. private void RegisteredListenersCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (IAxisListener obj in e.OldItems) { OnObjectUnregistered(obj); } } if (e.NewItems != null) { foreach (IAxisListener obj in e.NewItems) { OnObjectRegistered(obj); } } } /// /// This method is invoked when a series is registered. /// /// The series that has been registered. protected virtual void OnObjectRegistered(IAxisListener series) { } /// /// This method is invoked when a series is unregistered. /// /// The series that has been unregistered. protected virtual void OnObjectUnregistered(IAxisListener series) { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/AxisIntervalType.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Specifies an interval type. /// /// Preview internal enum AxisIntervalType { /// /// Automatically determined by the ISeriesHost control. /// Auto = 0, /// /// The interval type is numerical. /// Number = 1, /// /// The interval type is years. /// Years = 2, /// /// The interval type is months. /// Months = 3, /// /// The interval type is weeks. /// Weeks = 4, /// /// The interval type is days. /// Days = 5, /// /// The interval type is hours. /// Hours = 6, /// /// The interval type is minutes. /// Minutes = 7, /// /// The interval type is seconds. /// Seconds = 8, /// /// The interval type is milliseconds. /// Milliseconds = 9, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/AxisLabel.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { /// /// A label used to display data in an axis. /// public class AxisLabel : Control { #region public string StringFormat /// /// Gets or sets the text string format. /// public string StringFormat { get { return GetValue(StringFormatProperty) as string; } set { SetValue(StringFormatProperty, value); } } /// /// Identifies the StringFormat dependency property. /// public static readonly DependencyProperty StringFormatProperty = DependencyProperty.Register( "StringFormat", typeof(string), typeof(AxisLabel), new PropertyMetadata(null, OnStringFormatPropertyChanged)); /// /// StringFormatProperty property changed handler. /// /// AxisLabel that changed its StringFormat. /// Event arguments. private static void OnStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AxisLabel source = (AxisLabel)d; string newValue = (string)e.NewValue; source.OnStringFormatPropertyChanged(newValue); } /// /// StringFormatProperty property changed handler. /// /// New value. protected virtual void OnStringFormatPropertyChanged(string newValue) { UpdateFormattedContent(); } #endregion public string StringFormat #region public string FormattedContent /// /// Gets the formatted content property. /// public string FormattedContent { get { return GetValue(FormattedContentProperty) as string; } protected set { SetValue(FormattedContentProperty, value); } } /// /// Identifies the FormattedContent dependency property. /// public static readonly DependencyProperty FormattedContentProperty = DependencyProperty.Register( "FormattedContent", typeof(string), typeof(AxisLabel), new PropertyMetadata(null)); #endregion public string FormattedContent /// /// Initializes the static members of the AxisLabel class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static AxisLabel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AxisLabel), new FrameworkPropertyMetadata(typeof(AxisLabel))); } /// /// Instantiates a new instance of the AxisLabel class. /// public AxisLabel() { this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = StringFormat ?? "{0}" }); } /// /// Updates the formatted text. /// protected virtual void UpdateFormattedContent() { this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = StringFormat ?? "{0}" }); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/AxisLocation.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Axis position. /// public enum AxisLocation { /// /// Location is determined automatically. /// Auto, /// /// Left in the series host area. /// Left, /// /// Top in the series host area. /// Top, /// /// Right in the series host area. /// Right, /// /// Bottom of the series host area. /// Bottom, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/AxisOrientation.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Specifies the orientation of an axis. /// /// Preview public enum AxisOrientation { /// /// Orientation is automatically set. /// None, /// /// Indicates the axis plots along the X axis. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X", Justification = "X is the expected terminology.")] X, /// /// Indicates the axis plots along the Y axis. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = "Y is the expected terminology.")] Y, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/CategoryAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that displays categories. /// [StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(AxisLabel))] [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] [TemplatePart(Name = AxisGridName, Type = typeof(Grid))] [TemplatePart(Name = AxisTitleName, Type = typeof(Title))] public class CategoryAxis : DisplayAxis, ICategoryAxis { /// /// A pool of major tick marks. /// private ObjectPool _majorTickMarkPool; /// /// A pool of labels. /// private ObjectPool _labelPool; #region public CategorySortOrder SortOrder /// /// Gets or sets the sort order used for the categories. /// public CategorySortOrder SortOrder { get { return (CategorySortOrder)GetValue(SortOrderProperty); } set { SetValue(SortOrderProperty, value); } } /// /// Identifies the SortOrder dependency property. /// public static readonly DependencyProperty SortOrderProperty = DependencyProperty.Register( "SortOrder", typeof(CategorySortOrder), typeof(CategoryAxis), new PropertyMetadata(CategorySortOrder.None, OnSortOrderPropertyChanged)); /// /// SortOrderProperty property changed handler. /// /// CategoryAxis that changed its SortOrder. /// Event arguments. private static void OnSortOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { CategoryAxis source = (CategoryAxis)d; source.OnSortOrderPropertyChanged(); } /// /// SortOrderProperty property changed handler. /// private void OnSortOrderPropertyChanged() { Invalidate(); } #endregion public CategorySortOrder SortOrder /// /// Gets or sets a list of categories to display. /// private IList Categories { get; set; } /// /// Gets or sets the grid line coordinates to display. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] private IList GridLineCoordinatesToDisplay { get; set; } /// /// Instantiates a new instance of the CategoryAxis class. /// public CategoryAxis() { this._labelPool = new ObjectPool(() => CreateAxisLabel()); this._majorTickMarkPool = new ObjectPool(() => CreateMajorTickMark()); this.Categories = new List(); this.GridLineCoordinatesToDisplay = new List(); } /// /// Updates categories when a series is registered. /// /// The series to be registered. protected override void OnObjectRegistered(IAxisListener series) { base.OnObjectRegistered(series); if (series is IDataProvider) { UpdateCategories(); } } /// /// Updates categories when a series is unregistered. /// /// The series to be unregistered. protected override void OnObjectUnregistered(IAxisListener series) { base.OnObjectUnregistered(series); if (series is IDataProvider) { UpdateCategories(); } } /// /// Returns range of coordinates for a given category. /// /// The category to return the range for. /// The range of coordinates corresponding to the category. /// public Range GetPlotAreaCoordinateRange(object category) { if (category == null) { throw new ArgumentNullException("category"); } int index = Categories.IndexOf(category); if (index == -1) { return new Range(); } if (Orientation == AxisOrientation.X || Orientation == AxisOrientation.Y) { double maximumLength = Math.Max(ActualLength - 1, 0); double lower = (index * maximumLength) / Categories.Count; double upper = ((index + 1) * maximumLength) / Categories.Count; if (Orientation == AxisOrientation.X) { return new Range(new UnitValue(lower, Unit.Pixels), new UnitValue(upper, Unit.Pixels)); } else if (Orientation == AxisOrientation.Y) { return new Range(new UnitValue(maximumLength - upper, Unit.Pixels), new UnitValue(maximumLength - lower, Unit.Pixels)); } } else { double startingAngle = 270.0; double angleOffset = 360 / this.Categories.Count; double halfAngleOffset = angleOffset / 2.0; int categoryIndex = this.Categories.IndexOf(category); double angle = startingAngle + (categoryIndex * angleOffset); return new Range(new UnitValue(angle - halfAngleOffset, Unit.Degrees), new UnitValue(angle + halfAngleOffset, Unit.Degrees)); } return new Range(); } /// /// Returns the category at a given coordinate. /// /// The plot area position. /// The category at the given plot area position. public object GetCategoryAtPosition(UnitValue position) { if (this.ActualLength == 0.0 || this.Categories.Count == 0) { return null; } if (position.Unit == Unit.Pixels) { double coordinate = position.Value; int index = (int)Math.Floor(coordinate / (this.ActualLength / this.Categories.Count)); if (index >= 0 && index < this.Categories.Count) { if (Orientation == AxisOrientation.X) { return this.Categories[index]; } else { return this.Categories[(this.Categories.Count - 1) - index]; } } } else { throw new NotImplementedException(); } return null; } /// /// Updates the categories in response to an update from a registered /// axis data provider. /// /// The category axis information /// provider. /// A sequence of categories. public void DataChanged(IDataProvider dataProvider, IEnumerable data) { UpdateCategories(); } /// /// Updates the list of categories. /// private void UpdateCategories() { IEnumerable categories = this.RegisteredListeners .OfType() .SelectMany(infoProvider => infoProvider.GetData(this)) .Distinct(); if (SortOrder == CategorySortOrder.Ascending) { categories = categories.OrderBy(category => category); } else if (SortOrder == CategorySortOrder.Descending) { categories = categories.OrderByDescending(category => category); } this.Categories = categories.ToList(); Invalidate(); } /// /// Returns the major axis grid line coordinates. /// /// The available size. /// A sequence of the major grid line coordinates. protected override IEnumerable GetMajorGridLineCoordinates(Size availableSize) { return GridLineCoordinatesToDisplay; } /// /// The plot area coordinate of a value. /// /// The value for which to retrieve the plot area /// coordinate. /// The plot area coordinate. public override UnitValue GetPlotAreaCoordinate(object value) { if (value == null) { throw new ArgumentNullException("value"); } Range range = GetPlotAreaCoordinateRange(value); if (range.HasData) { double minimum = range.Minimum.Value; double maximum = range.Maximum.Value; return new UnitValue(((maximum - minimum) / 2.0) + minimum, range.Minimum.Unit); } else { return UnitValue.NaN(); } } /// /// Creates and prepares a new axis label. /// /// The axis label value. /// The axis label content control. private Control CreateAndPrepareAxisLabel(object value) { Control axisLabel = _labelPool.Next(); PrepareAxisLabel(axisLabel, value); return axisLabel; } /// /// Renders as an oriented axis. /// /// The available size. private void RenderOriented(Size availableSize) { _labelPool.Reset(); _majorTickMarkPool.Reset(); try { OrientedPanel.Children.Clear(); this.GridLineCoordinatesToDisplay.Clear(); if (this.Categories.Count > 0) { double maximumLength = Math.Max(GetLength(availableSize) - 1, 0); Action placeTickMarkAt = (pos) => { Line tickMark = _majorTickMarkPool.Next(); OrientedPanel.SetCenterCoordinate(tickMark, pos); OrientedPanel.SetPriority(tickMark, 0); this.GridLineCoordinatesToDisplay.Add(new UnitValue(pos, Unit.Pixels)); OrientedPanel.Children.Add(tickMark); }; int index = 0; int priority = 0; foreach (object category in Categories) { Control axisLabel = CreateAndPrepareAxisLabel(category); double lower = ((index * maximumLength) / Categories.Count) + 0.5; double upper = (((index + 1) * maximumLength) / Categories.Count) + 0.5; placeTickMarkAt(lower); OrientedPanel.SetCenterCoordinate(axisLabel, (lower + upper) / 2); OrientedPanel.SetPriority(axisLabel, priority + 1); OrientedPanel.Children.Add(axisLabel); index++; priority = (priority + 1) % 2; } placeTickMarkAt(maximumLength + 0.5); } } finally { _labelPool.Done(); _majorTickMarkPool.Done(); } } /// /// Renders the axis labels, tick marks, and other visual elements. /// /// The available size. protected override void Render(Size availableSize) { RenderOriented(availableSize); } /// /// Returns a value indicating whether a value can be plotted on the /// axis. /// /// A value which may or may not be able to be /// plotted. /// A value indicating whether a value can be plotted on the /// axis. public override bool CanPlot(object value) { return true; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/CategorySortOrder.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// The sort order to use when sorting categories. /// public enum CategorySortOrder { /// /// No sort order. /// None, /// /// Ascending sort order. /// Ascending, /// /// Descending sort order. /// Descending } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/DateTimeAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; using EF = System.Windows.Controls.DataVisualization.EnumerableFunctions; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that displays numeric values. /// [StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MinorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(DateTimeAxisLabel))] [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] [TemplatePart(Name = AxisGridName, Type = typeof(Grid))] [TemplatePart(Name = AxisTitleName, Type = typeof(Title))] public class DateTimeAxis : RangeAxis { #region public DateTime? ActualMaximum /// /// Gets the actual maximum value plotted on the chart. /// public DateTime? ActualMaximum { get { return (DateTime?)GetValue(ActualMaximumProperty); } private set { SetValue(ActualMaximumProperty, value); } } /// /// Identifies the ActualMaximum dependency property. /// public static readonly DependencyProperty ActualMaximumProperty = DependencyProperty.Register( "ActualMaximum", typeof(DateTime?), typeof(DateTimeAxis), null); #endregion public DateTime? ActualMaximum #region public DateTime? ActualMinimum /// /// Gets the actual maximum value plotted on the chart. /// public DateTime? ActualMinimum { get { return (DateTime?)GetValue(ActualMinimumProperty); } private set { SetValue(ActualMinimumProperty, value); } } /// /// Identifies the ActualMinimum dependency property. /// public static readonly DependencyProperty ActualMinimumProperty = DependencyProperty.Register( "ActualMinimum", typeof(DateTime?), typeof(DateTimeAxis), null); #endregion public DateTime? ActualMinimum #region public DateTime? Maximum /// /// Gets or sets the maximum value plotted on the axis. /// [TypeConverter(typeof(NullableConverter))] public DateTime? Maximum { get { return (DateTime?)GetValue(MaximumProperty); } set { SetValue(MaximumProperty, value); } } /// /// Identifies the Maximum dependency property. /// public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( "Maximum", typeof(DateTime?), typeof(DateTimeAxis), new PropertyMetadata(null, OnMaximumPropertyChanged)); /// /// MaximumProperty property changed handler. /// /// DateTimeAxis2 that changed its Maximum. /// Event arguments. private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxis source = (DateTimeAxis)d; DateTime? newValue = (DateTime?)e.NewValue; source.OnMaximumPropertyChanged(newValue); } /// /// MaximumProperty property changed handler. /// /// New value. private void OnMaximumPropertyChanged(DateTime? newValue) { this.ProtectedMaximum = newValue; } #endregion public DateTime? Maximum #region public DateTime? Minimum /// /// Gets or sets the minimum value to plot on the axis. /// [TypeConverter(typeof(NullableConverter))] public DateTime? Minimum { get { return (DateTime?)GetValue(MinimumProperty); } set { SetValue(MinimumProperty, value); } } /// /// Identifies the Minimum dependency property. /// public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( "Minimum", typeof(DateTime?), typeof(DateTimeAxis), new PropertyMetadata(null, OnMinimumPropertyChanged)); /// /// MinimumProperty property changed handler. /// /// DateTimeAxis2 that changed its Minimum. /// Event arguments. private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxis source = (DateTimeAxis)d; DateTime? newValue = (DateTime?)e.NewValue; source.OnMinimumPropertyChanged(newValue); } /// /// MinimumProperty property changed handler. /// /// New value. private void OnMinimumPropertyChanged(DateTime? newValue) { this.ProtectedMinimum = newValue; } #endregion public DateTime? Minimum #region public double? Interval /// /// Gets or sets the axis interval. /// [TypeConverter(typeof(NullableConverter))] public double? Interval { get { return (double?)GetValue(IntervalProperty); } set { SetValue(IntervalProperty, value); } } /// /// Identifies the Interval dependency property. /// public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register( "Interval", typeof(double?), typeof(DateTimeAxis), new PropertyMetadata(null, OnIntervalPropertyChanged)); /// /// IntervalProperty property changed handler. /// /// DateTimeAxis2 that changed its Interval. /// Event arguments. private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxis source = (DateTimeAxis)d; source.OnIntervalPropertyChanged(); } /// /// IntervalProperty property changed handler. /// private void OnIntervalPropertyChanged() { Invalidate(); } #endregion public double? Interval #region public double ActualInterval /// /// Gets the actual interval. /// public double ActualInterval { get { return (double)GetValue(ActualIntervalProperty); } private set { SetValue(ActualIntervalProperty, value); } } /// /// Identifies the ActualInterval dependency property. /// public static readonly DependencyProperty ActualIntervalProperty = DependencyProperty.Register( "ActualInterval", typeof(double), typeof(DateTimeAxis), new PropertyMetadata(double.NaN)); #endregion public double ActualInterval #region public DateTimeIntervalType IntervalType /// /// Gets or sets the interval to use for the axis. /// public DateTimeIntervalType IntervalType { get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); } set { SetValue(IntervalTypeProperty, value); } } /// /// Identifies the InternalIntervalType dependency property. /// public static readonly DependencyProperty IntervalTypeProperty = DependencyProperty.Register( "IntervalType", typeof(DateTimeIntervalType), typeof(DateTimeAxis), new PropertyMetadata(DateTimeIntervalType.Auto, OnIntervalTypePropertyChanged)); /// /// IntervalTypeProperty property changed handler. /// /// DateTimeAxis that changed its InternalIntervalType. /// Event arguments. private static void OnIntervalTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxis source = (DateTimeAxis)d; DateTimeIntervalType newValue = (DateTimeIntervalType)e.NewValue; source.OnIntervalTypePropertyChanged(newValue); } /// /// IntervalTypeProperty property changed handler. /// /// New value. private void OnIntervalTypePropertyChanged(DateTimeIntervalType newValue) { this.ActualIntervalType = newValue; Invalidate(); } #endregion public DateTimeIntervalType IntervalType #region public DateTimeIntervalType ActualIntervalType /// /// Gets or sets the actual interval type. /// private DateTimeIntervalType ActualIntervalType { get { return (DateTimeIntervalType)GetValue(ActualIntervalTypeProperty); } set { SetValue(ActualIntervalTypeProperty, value); } } /// /// Identifies the ActualIntervalType dependency property. /// private static readonly DependencyProperty ActualIntervalTypeProperty = DependencyProperty.Register( "ActualIntervalType", typeof(DateTimeIntervalType), typeof(DateTimeAxis), new PropertyMetadata(DateTimeIntervalType.Auto)); #endregion public DateTimeIntervalType ActualIntervalType /// /// Gets the origin value on the axis. /// protected override IComparable Origin { get { return null; } } /// /// Instantiates a new instance of the DateTimeAxis2 class. /// public DateTimeAxis() { int year = DateTime.Now.Year; this.ActualRange = new Range(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)); } /// /// Creates a new instance of the DateTimeAxisLabel class. /// /// Returns a new instance of the DateTimeAxisLabel class. /// protected override Control CreateAxisLabel() { return new DateTimeAxisLabel(); } /// /// Prepares an instance of the DateTimeAxisLabel class by setting its /// IntervalType property. /// /// An instance of the DateTimeAxisLabel class. /// /// The data context to assign to the label. /// protected override void PrepareAxisLabel(Control label, object dataContext) { DateTimeAxisLabel dateTimeAxisLabel = label as DateTimeAxisLabel; if (dateTimeAxisLabel != null) { dateTimeAxisLabel.IntervalType = ActualIntervalType; } base.PrepareAxisLabel(label, dataContext); } /// /// Gets the actual range of DateTime values. /// protected Range ActualDateTimeRange { get; private set; } /// /// Updates the typed actual maximum and minimum properties when the /// actual range changes. /// /// The actual range. protected override void OnActualRangeChanged(Range range) { ActualDateTimeRange = range.ToDateTimeRange(); if (range.HasData) { this.ActualMaximum = (DateTime)range.Maximum; this.ActualMinimum = (DateTime)range.Minimum; } else { this.ActualMaximum = null; this.ActualMinimum = null; } base.OnActualRangeChanged(range); } /// /// Returns a value indicating whether a value can plot. /// /// The value to plot. /// A value indicating whether a value can plot. public override bool CanPlot(object value) { DateTime val; return ValueHelper.TryConvert(value, out val); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The length of the axis. /// The plot area coordinate of a value. protected override UnitValue GetPlotAreaCoordinate(object value, double length) { return GetPlotAreaCoordinate(value, ActualDateTimeRange, length); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The range to use determine the coordinate. /// The length of the axis. /// The plot area coordinate of a value. protected override UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length) { return GetPlotAreaCoordinate(value, currentRange.ToDateTimeRange(), length); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The range to use determine the coordinate. /// The length of the axis. /// The plot area coordinate of a value. private static UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length) { if (currentRange.HasData) { DateTime dateTimeValue = ValueHelper.ToDateTime(value); double rangelength = currentRange.Maximum.ToOADate() - currentRange.Minimum.ToOADate(); double pixelLength = Math.Max(length - 1, 0); return new UnitValue((dateTimeValue.ToOADate() - currentRange.Minimum.ToOADate()) * (pixelLength / rangelength), Unit.Pixels); } return UnitValue.NaN(); } /// /// Returns the actual interval to use to determine which values are /// displayed in the axis. /// /// The available size. /// The actual interval to use to determine which values are /// displayed in the axis. /// private double CalculateActualInterval(Size availableSize) { if (Interval != null) { return Interval.Value; } DateTimeIntervalType intervalType; double interval = CalculateDateTimeInterval(ActualDateTimeRange.Minimum, ActualDateTimeRange.Maximum, out intervalType, availableSize); ActualIntervalType = intervalType; return interval; } /// /// Returns a sequence of major values. /// /// The available size. /// A sequence of major values. protected virtual IEnumerable GetMajorAxisValues(Size availableSize) { if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0) { yield break; } this.ActualInterval = CalculateActualInterval(availableSize); DateTime date = ActualDateTimeRange.Minimum; DateTime start = AlignIntervalStart(date, this.ActualInterval, ActualIntervalType); while (start < date) { start = IncrementDateTime(start, this.ActualInterval); } IEnumerable intermediateDates = EnumerableFunctions .Iterate(start, next => IncrementDateTime(next, this.ActualInterval)) .TakeWhile(current => ActualDateTimeRange.Contains(current)); foreach (DateTime current in intermediateDates) { yield return current; } } /// /// Returns a sequence of values to create major tick marks for. /// /// The available size. /// A sequence of values to create major tick marks for. /// protected override IEnumerable GetMajorTickMarkValues(Size availableSize) { return GetMajorAxisValues(availableSize).CastWrapper(); } /// /// Returns a sequence of values to plot on the axis. /// /// The available size. /// A sequence of values to plot on the axis. protected override IEnumerable GetLabelValues(Size availableSize) { return GetMajorAxisValues(availableSize).CastWrapper(); } /// /// This method accepts a date time and increments it. /// /// A date time. /// The interval used to increment the date time. /// /// The new date time. private DateTime IncrementDateTime(DateTime date, double interval) { DateTimeIntervalType intervalType = this.ActualIntervalType; TimeSpan span = new TimeSpan(0); DateTime result; if (intervalType == DateTimeIntervalType.Days) { span = TimeSpan.FromDays(interval); } else if (intervalType == DateTimeIntervalType.Hours) { span = TimeSpan.FromHours(interval); } else if (intervalType == DateTimeIntervalType.Milliseconds) { span = TimeSpan.FromMilliseconds(interval); } else if (intervalType == DateTimeIntervalType.Seconds) { span = TimeSpan.FromSeconds(interval); } else if (intervalType == DateTimeIntervalType.Minutes) { span = TimeSpan.FromMinutes(interval); } else if (intervalType == DateTimeIntervalType.Weeks) { span = TimeSpan.FromDays(7.0 * interval); } else if (intervalType == DateTimeIntervalType.Months) { // Special case handling when current date point // to the last day of the month bool lastMonthDay = false; if (date.Day == DateTime.DaysInMonth(date.Year, date.Month)) { lastMonthDay = true; } // Add specified amount of months date = date.AddMonths((int)Math.Floor(interval)); span = TimeSpan.FromDays(30.0 * (interval - Math.Floor(interval))); // Check if last month of the day was used if (lastMonthDay && span.Ticks == 0) { // Make sure the last day of the month is selected int daysInMobth = DateTime.DaysInMonth(date.Year, date.Month); date = date.AddDays(daysInMobth - date.Day); } } else if (intervalType == DateTimeIntervalType.Years) { date = date.AddYears((int)Math.Floor(interval)); span = TimeSpan.FromDays(365.0 * (interval - Math.Floor(interval))); } result = date.Add(span); return result; } /// /// Adjusts the beginning of the first interval depending on the type and size. /// /// Original start point. /// Interval size. /// Type of the interval (Month, Year, ...). /// /// Adjusted interval start position. /// private static DateTime AlignIntervalStart(DateTime start, double intervalSize, DateTimeIntervalType type) { // Do not adjust start position for these interval type if (type == DateTimeIntervalType.Auto) { return start; } // Get the beginning of the interval depending on type DateTime newStartDate = start; // Adjust the months interval depending on size if (intervalSize > 0.0 && intervalSize != 1.0) { if (type == DateTimeIntervalType.Months && intervalSize <= 12.0 && intervalSize > 1) { // Make sure that the beginning is aligned correctly for cases // like quarters and half years DateTime resultDate = newStartDate; DateTime sizeAdjustedDate = new DateTime(newStartDate.Year, 1, 1, 0, 0, 0); while (sizeAdjustedDate < newStartDate) { resultDate = sizeAdjustedDate; sizeAdjustedDate = sizeAdjustedDate.AddMonths((int)intervalSize); } newStartDate = resultDate; return newStartDate; } } // Check interval type switch (type) { case DateTimeIntervalType.Years: int year = (int)((int)(newStartDate.Year / intervalSize) * intervalSize); if (year <= 0) { year = 1; } newStartDate = new DateTime(year, 1, 1, 0, 0, 0); break; case DateTimeIntervalType.Months: int month = (int)((int)(newStartDate.Month / intervalSize) * intervalSize); if (month <= 0) { month = 1; } newStartDate = new DateTime(newStartDate.Year, month, 1, 0, 0, 0); break; case DateTimeIntervalType.Days: int day = (int)((int)(newStartDate.Day / intervalSize) * intervalSize); if (day <= 0) { day = 1; } newStartDate = new DateTime(newStartDate.Year, newStartDate.Month, day, 0, 0, 0); break; case DateTimeIntervalType.Hours: int hour = (int)((int)(newStartDate.Hour / intervalSize) * intervalSize); newStartDate = new DateTime( newStartDate.Year, newStartDate.Month, newStartDate.Day, hour, 0, 0); break; case DateTimeIntervalType.Minutes: int minute = (int)((int)(newStartDate.Minute / intervalSize) * intervalSize); newStartDate = new DateTime( newStartDate.Year, newStartDate.Month, newStartDate.Day, newStartDate.Hour, minute, 0); break; case DateTimeIntervalType.Seconds: int second = (int)((int)(newStartDate.Second / intervalSize) * intervalSize); newStartDate = new DateTime( newStartDate.Year, newStartDate.Month, newStartDate.Day, newStartDate.Hour, newStartDate.Minute, second, 0); break; case DateTimeIntervalType.Milliseconds: int milliseconds = (int)((int)(newStartDate.Millisecond / intervalSize) * intervalSize); newStartDate = new DateTime( newStartDate.Year, newStartDate.Month, newStartDate.Day, newStartDate.Hour, newStartDate.Minute, newStartDate.Second, milliseconds); break; case DateTimeIntervalType.Weeks: // Elements that have interval set to weeks should be aligned to the // nearest start of week no matter how many weeks is the interval. newStartDate = new DateTime( newStartDate.Year, newStartDate.Month, newStartDate.Day, 0, 0, 0); newStartDate = newStartDate.AddDays(-((int)newStartDate.DayOfWeek)); break; } return newStartDate; } /// /// Returns the value range given a plot area coordinate. /// /// The position. /// A range of values at that plot area coordinate. protected override IComparable GetValueAtPosition(UnitValue value) { if (ActualRange.HasData && ActualLength != 0.0) { double coordinate = value.Value; if (value.Unit == Unit.Pixels) { double minimumAsDouble = ActualDateTimeRange.Minimum.ToOADate(); double rangelength = ActualDateTimeRange.Maximum.ToOADate() - minimumAsDouble; DateTime output = DateTime.FromOADate((coordinate * (rangelength / ActualLength)) + minimumAsDouble); return output; } else { throw new NotImplementedException(); } } return null; } /// /// Recalculates a DateTime interval obtained from maximum and minimum. /// /// The minimum. /// The maximum. /// Date time interval type. /// The available size. /// Auto Interval. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "The method should inspect all variations of time span (millisec to year) and contains long case. Otherwise is simple and readable.")] private double CalculateDateTimeInterval(DateTime minimum, DateTime maximum, out DateTimeIntervalType type, Size availableSize) { DateTime dateTimeMin = minimum; DateTime dateTimeMax = maximum; TimeSpan timeSpan = dateTimeMax.Subtract(dateTimeMin); // this algorithm is designed to return close to 10 intervals. // we need to align the time span for PrefferedNumberOfIntervals double maxIntervals = Orientation == AxisOrientation.X ? MaximumAxisIntervalsPer200Pixels * 0.8 : MaximumAxisIntervalsPer200Pixels; double rangeMultiplicator = GetLength(availableSize) / (200 * 10 / maxIntervals); timeSpan = new TimeSpan((long)((double)timeSpan.Ticks / rangeMultiplicator)); // Minutes double inter = timeSpan.TotalMinutes; // For Range less than 60 seconds interval is 5 sec if (inter <= 1.0) { // Milli Seconds double milliSeconds = timeSpan.TotalMilliseconds; if (milliSeconds <= 10) { type = DateTimeIntervalType.Milliseconds; return 1; } if (milliSeconds <= 50) { type = DateTimeIntervalType.Milliseconds; return 4; } if (milliSeconds <= 200) { type = DateTimeIntervalType.Milliseconds; return 20; } if (milliSeconds <= 500) { type = DateTimeIntervalType.Milliseconds; return 50; } // Seconds double seconds = timeSpan.TotalSeconds; if (seconds <= 7) { type = DateTimeIntervalType.Seconds; return 1; } else if (seconds <= 15) { type = DateTimeIntervalType.Seconds; return 2; } else if (seconds <= 30) { type = DateTimeIntervalType.Seconds; return 5; } else if (seconds <= 60) { type = DateTimeIntervalType.Seconds; return 10; } } else if (inter <= 2.0) { // For Range less than 120 seconds interval is 10 sec type = DateTimeIntervalType.Seconds; return 20; } else if (inter <= 3.0) { // For Range less than 180 seconds interval is 30 sec type = DateTimeIntervalType.Seconds; return 30; } else if (inter <= 10) { // For Range less than 10 minutes interval is 1 min type = DateTimeIntervalType.Minutes; return 1; } else if (inter <= 20) { // For Range less than 20 minutes interval is 1 min type = DateTimeIntervalType.Minutes; return 2; } else if (inter <= 60) { // For Range less than 60 minutes interval is 5 min type = DateTimeIntervalType.Minutes; return 5; } else if (inter <= 120) { // For Range less than 120 minutes interval is 10 min type = DateTimeIntervalType.Minutes; return 10; } else if (inter <= 180) { // For Range less than 180 minutes interval is 30 min type = DateTimeIntervalType.Minutes; return 30; } else if (inter <= 60 * 12) { // For Range less than 12 hours interval is 1 hour type = DateTimeIntervalType.Hours; return 1; } else if (inter <= 60 * 24) { // For Range less than 24 hours interval is 4 hour type = DateTimeIntervalType.Hours; return 4; } else if (inter <= 60 * 24 * 2) { // For Range less than 2 days interval is 6 hour type = DateTimeIntervalType.Hours; return 6; } else if (inter <= 60 * 24 * 3) { // For Range less than 3 days interval is 12 hour type = DateTimeIntervalType.Hours; return 12; } else if (inter <= 60 * 24 * 10) { // For Range less than 10 days interval is 1 day type = DateTimeIntervalType.Days; return 1; } else if (inter <= 60 * 24 * 20) { // For Range less than 20 days interval is 2 day type = DateTimeIntervalType.Days; return 2; } else if (inter <= 60 * 24 * 30) { // For Range less than 30 days interval is 3 day type = DateTimeIntervalType.Days; return 3; } else if (inter <= 60 * 24 * 30.5 * 2) { // For Range less than 2 months interval is 1 week type = DateTimeIntervalType.Weeks; return 1; } else if (inter <= 60 * 24 * 30.5 * 5) { // For Range less than 5 months interval is 2weeks type = DateTimeIntervalType.Weeks; return 2; } else if (inter <= 60 * 24 * 30.5 * 12) { // For Range less than 12 months interval is 1 month type = DateTimeIntervalType.Months; return 1; } else if (inter <= 60 * 24 * 30.5 * 24) { // For Range less than 24 months interval is 3 month type = DateTimeIntervalType.Months; return 3; } else if (inter <= 60 * 24 * 30.5 * 48) { // For Range less than 48 months interval is 6 months type = DateTimeIntervalType.Months; return 6; } // For Range more than 48 months interval is year type = DateTimeIntervalType.Years; double years = inter / 60 / 24 / 365; if (years < 5) { return 1; } else if (years < 10) { return 2; } // Make a correction of the interval return Math.Floor(years / 5); } /// /// Overrides the actual range to ensure that it is never set to an /// empty range. /// /// The range to override. /// The overridden range. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This method is very difficult to break up cleanly.")] protected override Range OverrideDataRange(Range range) { Range overriddenActualRange = range; if (!overriddenActualRange.HasData) { int year = DateTime.Now.Year; return new Range(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)); } else if (ValueHelper.Compare(overriddenActualRange.Minimum, overriddenActualRange.Maximum) == 0) { DateTime minimum = ValueHelper.ToDateTime(overriddenActualRange.Minimum); DateTime midpoint = ((DateTime.MinValue == minimum) ? DateTime.Now : minimum).Date; return new Range(midpoint.AddMonths(-6), midpoint.AddMonths(6)); } // ActualLength of 1.0 or less maps all points to the same coordinate if (range.HasData && this.ActualLength > 1.0) { IList valueMargins = new List(); foreach (ValueMargin valueMargin in this.RegisteredListeners .OfType() .SelectMany(provider => provider.GetValueMargins(this))) { valueMargins.Add( new ValueMarginCoordinateAndOverlap { ValueMargin = valueMargin, }); } if (valueMargins.Count > 0) { double maximumPixelMarginLength = valueMargins .Select(valueMargin => valueMargin.ValueMargin.LowMargin + valueMargin.ValueMargin.HighMargin) .MaxOrNullable().Value; // Requested margin is larger than the axis so give up // trying to find a range that will fit it. if (maximumPixelMarginLength > this.ActualLength) { return range; } Range currentRange = range.ToDateTimeRange(); // Ensure range is not empty. if (currentRange.Minimum == currentRange.Maximum) { int year = DateTime.Now.Year; currentRange = new Range(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)); } // priming the loop double actualLength = this.ActualLength; ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin; ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin; UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); while (maxLeftOverlapValueMargin.LeftOverlap > 0 || maxRightOverlapValueMargin.RightOverlap > 0) { long unitOverPixels = currentRange.GetLength().Value.Ticks / ((long) actualLength); DateTime newMinimum = new DateTime(currentRange.Minimum.Ticks - (long)((maxLeftOverlapValueMargin.LeftOverlap + 0.5) * unitOverPixels)); DateTime newMaximum = new DateTime(currentRange.Maximum.Ticks + (long)((maxRightOverlapValueMargin.RightOverlap + 0.5) * unitOverPixels)); currentRange = new Range(newMinimum, newMaximum); UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); } return currentRange.ToComparableRange(); } } return range; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/DateTimeAxisLabel.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis label for displaying DateTime values. /// public class DateTimeAxisLabel : AxisLabel { #region public DateTimeIntervalType IntervalType /// /// Gets or sets the interval type of the DateTimeAxis2. /// public DateTimeIntervalType IntervalType { get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); } set { SetValue(IntervalTypeProperty, value); } } /// /// Identifies the IntervalType dependency property. /// public static readonly System.Windows.DependencyProperty IntervalTypeProperty = System.Windows.DependencyProperty.Register( "IntervalType", typeof(DateTimeIntervalType), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(DateTimeIntervalType.Auto, OnIntervalTypePropertyChanged)); /// /// IntervalTypeProperty property changed handler. /// /// DateTimeAxisLabel that changed its IntervalType. /// Event arguments. private static void OnIntervalTypePropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; DateTimeIntervalType oldValue = (DateTimeIntervalType)e.OldValue; DateTimeIntervalType newValue = (DateTimeIntervalType)e.NewValue; source.OnIntervalTypePropertyChanged(oldValue, newValue); } /// /// IntervalTypeProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIntervalTypePropertyChanged(DateTimeIntervalType oldValue, DateTimeIntervalType newValue) { UpdateFormattedContent(); } #endregion public DateTimeIntervalType IntervalType #region public string YearsIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string YearsIntervalStringFormat { get { return GetValue(YearsIntervalStringFormatProperty) as string; } set { SetValue(YearsIntervalStringFormatProperty, value); } } /// /// Identifies the YearsIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty YearsIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "YearsIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnYearsIntervalStringFormatPropertyChanged)); /// /// YearsIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its YearsIntervalStringFormat. /// Event arguments. private static void OnYearsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnYearsIntervalStringFormatPropertyChanged(); } /// /// YearsIntervalStringFormatProperty property changed handler. /// protected virtual void OnYearsIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string YearsIntervalStringFormat #region public string MonthsIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string MonthsIntervalStringFormat { get { return GetValue(MonthsIntervalStringFormatProperty) as string; } set { SetValue(MonthsIntervalStringFormatProperty, value); } } /// /// Identifies the MonthsIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty MonthsIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "MonthsIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnMonthsIntervalStringFormatPropertyChanged)); /// /// MonthsIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its MonthsIntervalStringFormat. /// Event arguments. private static void OnMonthsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnMonthsIntervalStringFormatPropertyChanged(); } /// /// MonthsIntervalStringFormatProperty property changed handler. /// protected virtual void OnMonthsIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string MonthsIntervalStringFormat #region public string WeeksIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string WeeksIntervalStringFormat { get { return GetValue(WeeksIntervalStringFormatProperty) as string; } set { SetValue(WeeksIntervalStringFormatProperty, value); } } /// /// Identifies the WeeksIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty WeeksIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "WeeksIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnWeeksIntervalStringFormatPropertyChanged)); /// /// WeeksIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its WeeksIntervalStringFormat. /// Event arguments. private static void OnWeeksIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnWeeksIntervalStringFormatPropertyChanged(); } /// /// WeeksIntervalStringFormatProperty property changed handler. /// protected virtual void OnWeeksIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string WeeksIntervalStringFormat #region public string DaysIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string DaysIntervalStringFormat { get { return GetValue(DaysIntervalStringFormatProperty) as string; } set { SetValue(DaysIntervalStringFormatProperty, value); } } /// /// Identifies the DaysIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty DaysIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "DaysIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnDaysIntervalStringFormatPropertyChanged)); /// /// DaysIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its DaysIntervalStringFormat. /// Event arguments. private static void OnDaysIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnDaysIntervalStringFormatPropertyChanged(); } /// /// DaysIntervalStringFormatProperty property changed handler. /// protected virtual void OnDaysIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string DaysIntervalStringFormat #region public string HoursIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string HoursIntervalStringFormat { get { return GetValue(HoursIntervalStringFormatProperty) as string; } set { SetValue(HoursIntervalStringFormatProperty, value); } } /// /// Identifies the HoursIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty HoursIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "HoursIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnHoursIntervalStringFormatPropertyChanged)); /// /// HoursIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its HoursIntervalStringFormat. /// Event arguments. private static void OnHoursIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnHoursIntervalStringFormatPropertyChanged(); } /// /// HoursIntervalStringFormatProperty property changed handler. /// protected virtual void OnHoursIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string HoursIntervalStringFormat #region public string MinutesIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string MinutesIntervalStringFormat { get { return GetValue(MinutesIntervalStringFormatProperty) as string; } set { SetValue(MinutesIntervalStringFormatProperty, value); } } /// /// Identifies the MinutesIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty MinutesIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "MinutesIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnMinutesIntervalStringFormatPropertyChanged)); /// /// MinutesIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its MinutesIntervalStringFormat. /// Event arguments. private static void OnMinutesIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnMinutesIntervalStringFormatPropertyChanged(); } /// /// MinutesIntervalStringFormatProperty property changed handler. /// protected virtual void OnMinutesIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string MinutesIntervalStringFormat #region public string SecondsIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string SecondsIntervalStringFormat { get { return GetValue(SecondsIntervalStringFormatProperty) as string; } set { SetValue(SecondsIntervalStringFormatProperty, value); } } /// /// Identifies the SecondsIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty SecondsIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "SecondsIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnSecondsIntervalStringFormatPropertyChanged)); /// /// SecondsIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its SecondsIntervalStringFormat. /// Event arguments. private static void OnSecondsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnSecondsIntervalStringFormatPropertyChanged(); } /// /// SecondsIntervalStringFormatProperty property changed handler. /// protected virtual void OnSecondsIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string SecondsIntervalStringFormat #region public string MillisecondsIntervalStringFormat /// /// Gets or sets the format string to use when the interval is hours. /// public string MillisecondsIntervalStringFormat { get { return GetValue(MillisecondsIntervalStringFormatProperty) as string; } set { SetValue(MillisecondsIntervalStringFormatProperty, value); } } /// /// Identifies the MillisecondsIntervalStringFormat dependency property. /// public static readonly System.Windows.DependencyProperty MillisecondsIntervalStringFormatProperty = System.Windows.DependencyProperty.Register( "MillisecondsIntervalStringFormat", typeof(string), typeof(DateTimeAxisLabel), new System.Windows.PropertyMetadata(null, OnMillisecondsIntervalStringFormatPropertyChanged)); /// /// MillisecondsIntervalStringFormatProperty property changed handler. /// /// DateTimeAxisLabel that changed its MillisecondsIntervalStringFormat. /// Event arguments. private static void OnMillisecondsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeAxisLabel source = (DateTimeAxisLabel)d; source.OnMillisecondsIntervalStringFormatPropertyChanged(); } /// /// MillisecondsIntervalStringFormatProperty property changed handler. /// protected virtual void OnMillisecondsIntervalStringFormatPropertyChanged() { UpdateFormattedContent(); } #endregion public string MillisecondsIntervalStringFormat /// /// Initializes the static members of the DateTimeAxisLabel class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static DateTimeAxisLabel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimeAxisLabel), new FrameworkPropertyMetadata(typeof(DateTimeAxisLabel))); } /// /// Instantiates a new instance of the DateTimeAxisLabel class. /// public DateTimeAxisLabel() { } /// /// Updates the formatted text. /// [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Code is not overly complex.")] protected override void UpdateFormattedContent() { if (StringFormat == null) { switch (IntervalType) { case DateTimeIntervalType.Years: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = YearsIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Months: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = MonthsIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Weeks: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = WeeksIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Days: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = DaysIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Hours: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = HoursIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Minutes: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = MinutesIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Seconds: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = SecondsIntervalStringFormat ?? StringFormat ?? "{0}" }); break; case DateTimeIntervalType.Milliseconds: this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = MillisecondsIntervalStringFormat ?? StringFormat ?? "{0}" }); break; default: base.UpdateFormattedContent(); break; } } else { base.UpdateFormattedContent(); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/DateTimeIntervalType.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// A date time interval. /// public enum DateTimeIntervalType { /// /// Automatically determine interval. /// Auto = 0, /// /// Interval type is milliseconds. /// Milliseconds = 1, /// /// Interval type is seconds. /// Seconds = 2, /// /// Interval type is minutes. /// Minutes = 3, /// /// Interval type is hours. /// Hours = 4, /// /// Interval type is days. /// Days = 5, /// /// Interval type is weeks. /// Weeks = 6, /// /// Interval type is months. /// Months = 7, /// /// Interval type is years. /// Years = 8, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/DisplayAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Media; using System.Windows.Controls; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that has a range. /// public abstract class DisplayAxis : Axis, IRequireSeriesHost { /// /// Maximum intervals per 200 pixels. /// protected const double MaximumAxisIntervalsPer200Pixels = 8; /// /// The name of the axis grid template part. /// protected const string AxisGridName = "AxisGrid"; /// /// The name of the axis title template part. /// protected const string AxisTitleName = "AxisTitle"; #region public Style AxisLabelStyle /// /// Gets or sets the style used for the axis labels. /// public Style AxisLabelStyle { get { return GetValue(AxisLabelStyleProperty) as Style; } set { SetValue(AxisLabelStyleProperty, value); } } /// /// Identifies the AxisLabelStyle dependency property. /// public static readonly DependencyProperty AxisLabelStyleProperty = DependencyProperty.Register( "AxisLabelStyle", typeof(Style), typeof(DisplayAxis), new PropertyMetadata(null, OnAxisLabelStylePropertyChanged)); /// /// AxisLabelStyleProperty property changed handler. /// /// DisplayAxis that changed its AxisLabelStyle. /// Event arguments. private static void OnAxisLabelStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DisplayAxis source = (DisplayAxis)d; Style oldValue = (Style)e.OldValue; Style newValue = (Style)e.NewValue; source.OnAxisLabelStylePropertyChanged(oldValue, newValue); } /// /// AxisLabelStyleProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnAxisLabelStylePropertyChanged(Style oldValue, Style newValue) { } #endregion public Style AxisLabelStyle /// /// Gets the actual length. /// protected double ActualLength { get { return GetLength(new Size(this.ActualWidth, this.ActualHeight)); } } /// /// Returns the length of the axis given an available size. /// /// The available size. /// The length of the axis given an available size. protected double GetLength(Size availableSize) { if (this.ActualHeight == 0.0 && this.ActualWidth == 0.0) { return 0.0; } if (this.Orientation == AxisOrientation.X) { return availableSize.Width; } else if (this.Orientation == AxisOrientation.Y) { return availableSize.Height; } else { throw new InvalidOperationException(Properties.Resources.DisplayAxis_GetLength_CannotDetermineTheLengthOfAnAxisWithAnOrientationOfNone); } } #region private GridLines GridLines /// /// This field stores the grid lines element. /// private DisplayAxisGridLines _gridLines; /// /// Gets or sets the grid lines property. /// internal DisplayAxisGridLines GridLines { get { return _gridLines; } set { if (value != _gridLines) { DisplayAxisGridLines oldValue = _gridLines; _gridLines = value; OnGridLinesPropertyChanged(oldValue, value); } } } /// /// GridLinesProperty property changed handler. /// /// Old value. /// New value. private void OnGridLinesPropertyChanged(DisplayAxisGridLines oldValue, DisplayAxisGridLines newValue) { if (SeriesHost != null && oldValue != null) { SeriesHost.BackgroundElements.Remove(oldValue); } if (SeriesHost != null && newValue != null) { SeriesHost.BackgroundElements.Add(newValue); } } #endregion private GridLines GridLines #region public Style MajorTickMarkStyle /// /// Gets or sets the style applied to the Axis tick marks. /// /// The Style applied to the Axis tick marks. public Style MajorTickMarkStyle { get { return GetValue(MajorTickMarkStyleProperty) as Style; } set { SetValue(MajorTickMarkStyleProperty, value); } } /// /// Identifies the MajorTickMarkStyle dependency property. /// public static readonly DependencyProperty MajorTickMarkStyleProperty = DependencyProperty.Register( "MajorTickMarkStyle", typeof(Style), typeof(DisplayAxis), new PropertyMetadata(null, OnMajorTickMarkStylePropertyChanged)); /// /// MajorTickMarkStyleProperty property changed handler. /// /// DisplayAxis that changed its MajorTickMarkStyle. /// Event arguments. private static void OnMajorTickMarkStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DisplayAxis source = (DisplayAxis)d; Style oldValue = (Style)e.OldValue; Style newValue = (Style)e.NewValue; source.OnMajorTickMarkStylePropertyChanged(oldValue, newValue); } /// /// MajorTickMarkStyleProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnMajorTickMarkStylePropertyChanged(Style oldValue, Style newValue) { } #endregion public Style MajorTickMarkStyle #region public object Title /// /// Gets or sets the title property. /// public object Title { get { return GetValue(TitleProperty) as object; } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof(object), typeof(DisplayAxis), new PropertyMetadata(null, OnTitlePropertyChanged)); /// /// TitleProperty property changed handler. /// /// DisplayAxis that changed its Title. /// Event arguments. private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DisplayAxis source = (DisplayAxis)d; object oldValue = (object)e.OldValue; object newValue = (object)e.NewValue; source.OnTitlePropertyChanged(oldValue, newValue); } /// /// TitleProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnTitlePropertyChanged(object oldValue, object newValue) { if (this.AxisTitle != null) { this.AxisTitle.Content = Title; } } #endregion public object Title /// /// Gets or sets the LayoutTransformControl used to rotate the title. /// private LayoutTransformControl TitleLayoutTransformControl { get; set; } #region public Style TitleStyle /// /// Gets or sets the style applied to the Axis title. /// /// The Style applied to the Axis title. public Style TitleStyle { get { return GetValue(TitleStyleProperty) as Style; } set { SetValue(TitleStyleProperty, value); } } /// /// Identifies the TitleStyle dependency property. /// public static readonly DependencyProperty TitleStyleProperty = DependencyProperty.Register( "TitleStyle", typeof(Style), typeof(DisplayAxis), null); #endregion #region public bool ShowGridLines /// /// Gets or sets a value indicating whether grid lines should be shown. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected casing.")] public bool ShowGridLines { get { return (bool)GetValue(ShowGridLinesProperty); } set { SetValue(ShowGridLinesProperty, value); } } /// /// Identifies the ShowGridLines dependency property. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected capitalization.")] [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] public static readonly DependencyProperty ShowGridLinesProperty = DependencyProperty.Register( "ShowGridLines", typeof(bool), typeof(DisplayAxis), new PropertyMetadata(false, OnShowGridLinesPropertyChanged)); /// /// ShowGridLinesProperty property changed handler. /// /// Axis that changed its ShowGridLines. /// Event arguments. private static void OnShowGridLinesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DisplayAxis source = (DisplayAxis)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnShowGridLinesPropertyChanged(oldValue, newValue); } /// /// ShowGridLinesProperty property changed handler. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected capitalization.")] protected virtual void OnShowGridLinesPropertyChanged(bool oldValue, bool newValue) { SetShowGridLines(newValue); } #endregion public bool ShowGridLines /// /// Creates and destroys a grid lines element based on the specified /// value. /// /// A value indicating whether to display grid /// lines or not. private void SetShowGridLines(bool newValue) { if (newValue == true) { this.GridLines = new OrientedAxisGridLines(this); } else { this.GridLines = null; } } #region public Style GridLineStyle /// /// Gets or sets the Style of the Axis's gridlines. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "Current casing is the expected one.")] public Style GridLineStyle { get { return GetValue(GridLineStyleProperty) as Style; } set { SetValue(GridLineStyleProperty, value); } } /// /// Identifies the GridlineStyle dependency property. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "Current casing is the expected one.")] public static readonly DependencyProperty GridLineStyleProperty = DependencyProperty.Register( "GridLineStyle", typeof(Style), typeof(DisplayAxis), null); #endregion /// /// The grid used to layout the axis. /// private Grid _grid; /// /// Gets or sets the grid used to layout the axis. /// private Grid AxisGrid { get { return _grid; } set { if (_grid != value) { if (_grid != null) { _grid.Children.Clear(); } _grid = value; if (_grid != null) { _grid.Children.Add(this.OrientedPanel); if (this.AxisTitle != null) { _grid.Children.Add(this.AxisTitle); } } } } } /// /// Gets or sets a grid to lay out the dependent axis. /// private Grid DependentAxisGrid { get; set; } /// /// Gets the oriented panel used to layout the axis labels. /// internal OrientedPanel OrientedPanel { get; private set; } /// /// The control used to display the axis title. /// private Title _axisTitle; /// /// Gets or sets the title control used to display the title. /// private Title AxisTitle { get { return _axisTitle; } set { if (_axisTitle != value) { if (_axisTitle != null) { _axisTitle.Content = null; } _axisTitle = value; if (Title != null) { _axisTitle.Content = Title; } } } } /// /// Creates a major axis tick mark. /// /// A line to used to render a tick mark. protected virtual Line CreateMajorTickMark() { return CreateTickMark(MajorTickMarkStyle); } /// /// Creates a tick mark and applies a style to it. /// /// The style to apply. /// The newly created tick mark. protected Line CreateTickMark(Style style) { Line line = new Line(); line.Style = style; if (this.Orientation == AxisOrientation.Y) { line.Y1 = 0.5; line.Y2 = 0.5; } else if (this.Orientation == AxisOrientation.X) { line.X1 = 0.5; line.X2 = 0.5; } return line; } /// /// This method is used to share the grid line coordinates with the /// internal grid lines control. /// /// A sequence of the major grid line coordinates. internal IEnumerable InternalGetMajorGridLinePositions() { return GetMajorGridLineCoordinates(new Size(this.ActualWidth, this.ActualHeight)); } /// /// Returns the coordinates to use for the grid line control. /// /// The available size. /// A sequence of coordinates at which to draw grid lines. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Returns the coordinates of the grid lines.")] protected abstract IEnumerable GetMajorGridLineCoordinates(Size availableSize); /// /// Initializes the static members of the DisplayAxis class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static DisplayAxis() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DisplayAxis), new FrameworkPropertyMetadata(typeof(DisplayAxis))); } /// /// Instantiates a new instance of the DisplayAxis class. /// protected DisplayAxis() { this.OrientedPanel = new OrientedPanel(); this.DependentAxisGrid = new Grid(); this.TitleLayoutTransformControl = new LayoutTransformControl(); this.TitleLayoutTransformControl.HorizontalAlignment = HorizontalAlignment.Center; this.TitleLayoutTransformControl.VerticalAlignment = VerticalAlignment.Center; this.SizeChanged += new SizeChangedEventHandler(DisplayAxisSizeChanged); } /// /// If display axis has just become visible, invalidate. /// /// The source of the event. /// Information about the event. private void DisplayAxisSizeChanged(object sender, SizeChangedEventArgs e) { if (e.PreviousSize.Width == 0.0 && e.PreviousSize.Height == 0.0) { Invalidate(); } } /// /// Creates an axis label. /// /// The new axis label. protected virtual Control CreateAxisLabel() { return new AxisLabel(); } /// /// Updates the grid lines element if a suitable dependent axis has /// been added to a radial axis. /// protected override void OnDependentAxesCollectionChanged() { SetShowGridLines(ShowGridLines); base.OnDependentAxesCollectionChanged(); } /// /// Prepares an axis label to be plotted. /// /// The axis label to prepare. /// The data context to use for the axis /// label. protected virtual void PrepareAxisLabel(Control label, object dataContext) { label.DataContext = dataContext; label.SetStyle(AxisLabelStyle); } /// /// Retrieves template parts and configures layout. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); this.AxisGrid = GetTemplateChild(AxisGridName) as Grid; this.AxisTitle = GetTemplateChild(AxisTitleName) as Title; if (this.AxisTitle != null && this.AxisGrid.Children.Contains(this.AxisTitle)) { this.AxisGrid.Children.Remove(this.AxisTitle); this.TitleLayoutTransformControl.Child = this.AxisTitle; this.AxisGrid.Children.Add(this.TitleLayoutTransformControl); } ArrangeAxisGrid(); } /// /// When the size of the oriented panel changes invalidate the axis. /// /// The source of the event. /// Information about the event. private void OnOrientedPanelSizeChanged(object sender, SizeChangedEventArgs e) { Invalidate(); } /// /// Arranges the grid when the location property is changed. /// /// The old location. /// The new location. protected override void OnLocationPropertyChanged(AxisLocation oldValue, AxisLocation newValue) { ArrangeAxisGrid(); base.OnLocationPropertyChanged(oldValue, newValue); } /// /// Arranges the elements in the axis grid. /// private void ArrangeAxisGrid() { if (this.AxisGrid != null) { this.AxisGrid.ColumnDefinitions.Clear(); this.AxisGrid.RowDefinitions.Clear(); this.AxisGrid.Children.Clear(); if (this.Orientation == AxisOrientation.Y) { this.OrientedPanel.Orientation = System.Windows.Controls.Orientation.Vertical; this.OrientedPanel.IsReversed = true; if (this.Location == AxisLocation.Left || this.Location == AxisLocation.Right) { this.TitleLayoutTransformControl.Transform = new RotateTransform { Angle = -90.0 }; this.OrientedPanel.IsInverted = !(Location == AxisLocation.Right); this.AxisGrid.ColumnDefinitions.Add(new ColumnDefinition()); this.AxisGrid.RowDefinitions.Add(new RowDefinition()); int column = 0; if (this.AxisTitle != null) { this.AxisGrid.ColumnDefinitions.Add(new ColumnDefinition()); Grid.SetRow(this.TitleLayoutTransformControl, 0); Grid.SetColumn(this.TitleLayoutTransformControl, 0); column++; } Grid.SetRow(this.OrientedPanel, 0); Grid.SetColumn(this.OrientedPanel, column); this.AxisGrid.Children.Add(this.TitleLayoutTransformControl); this.AxisGrid.Children.Add(this.OrientedPanel); if (this.Location == AxisLocation.Right) { AxisGrid.Mirror(System.Windows.Controls.Orientation.Vertical); this.TitleLayoutTransformControl.Transform = new RotateTransform { Angle = 90 }; } } } else if (this.Orientation == AxisOrientation.X) { this.OrientedPanel.Orientation = System.Windows.Controls.Orientation.Horizontal; this.OrientedPanel.IsReversed = false; if (this.Location == AxisLocation.Top || this.Location == AxisLocation.Bottom) { this.OrientedPanel.IsInverted = (Location == AxisLocation.Top); this.TitleLayoutTransformControl.Transform = new RotateTransform { Angle = 0 }; this.AxisGrid.ColumnDefinitions.Add(new ColumnDefinition()); this.AxisGrid.RowDefinitions.Add(new RowDefinition()); if (this.AxisTitle != null) { this.AxisGrid.RowDefinitions.Add(new RowDefinition()); Grid.SetColumn(this.TitleLayoutTransformControl, 0); Grid.SetRow(this.TitleLayoutTransformControl, 1); } Grid.SetColumn(this.OrientedPanel, 0); Grid.SetRow(this.OrientedPanel, 0); this.AxisGrid.Children.Add(this.TitleLayoutTransformControl); this.AxisGrid.Children.Add(this.OrientedPanel); if (this.Location == AxisLocation.Top) { AxisGrid.Mirror(System.Windows.Controls.Orientation.Horizontal); } } } Invalidate(); } } /// /// Renders the axis. /// /// The available size. /// The required size. protected override Size MeasureOverride(Size availableSize) { RenderAxis(availableSize); return base.MeasureOverride(availableSize); } /// /// Reformulates the grid when the orientation is changed. Grid is /// either separated into two columns or two rows. The title is /// inserted with the outermost section from the edge and an oriented /// panel is inserted into the innermost section. /// /// The old value. /// The new value. protected override void OnOrientationPropertyChanged(AxisOrientation oldValue, AxisOrientation newValue) { ArrangeAxisGrid(); base.OnOrientationPropertyChanged(oldValue, newValue); } /// /// Updates the visual appearance of the axis when it is invalidated. /// /// Information for the invalidated event. protected override void OnInvalidated(RoutedEventArgs args) { InvalidateMeasure(); base.OnInvalidated(args); } /// /// Renders the axis if there is a valid value for orientation. /// /// The available size in which to render /// the axis. private void RenderAxis(Size availableSize) { if (Orientation != AxisOrientation.None && Location != AxisLocation.Auto) { Render(availableSize); } } /// /// Renders the axis labels, tick marks, and other visual elements. /// /// The available size. protected abstract void Render(Size availableSize); /// /// Invalidates the axis. /// protected void Invalidate() { OnInvalidated(new RoutedEventArgs()); } /// /// The series host. /// private ISeriesHost _seriesHost; /// /// Gets or sets the series host. /// public ISeriesHost SeriesHost { get { return _seriesHost; } set { if (value != _seriesHost) { ISeriesHost oldValue = _seriesHost; _seriesHost = value; OnSeriesHostPropertyChanged(oldValue, value); } } } /// /// This method is run when the series host property is changed. /// /// The old series host. /// The new series host. protected virtual void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { if (oldValue != null && this.GridLines != null) { oldValue.BackgroundElements.Remove(this.GridLines); } if (newValue != null && this.GridLines != null) { newValue.BackgroundElements.Add(this.GridLines); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/DisplayAxisGridLines.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// This control draws gridlines with the help of an axis. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "DisplayAxisGridLines", Justification = "This is the expected capitalization.")] internal abstract class DisplayAxisGridLines : Canvas, IAxisListener { #region public DisplayAxis Axis /// /// The field that stores the axis that the grid lines are connected to. /// private DisplayAxis _axis; /// /// Gets the axis that the grid lines are connected to. /// public DisplayAxis Axis { get { return _axis; } private set { if (_axis != value) { DisplayAxis oldValue = _axis; _axis = value; if (oldValue != _axis) { OnAxisPropertyChanged(oldValue, value); } } } } /// /// AxisProperty property changed handler. /// /// Old value. /// New value. private void OnAxisPropertyChanged(DisplayAxis oldValue, DisplayAxis newValue) { Debug.Assert(newValue != null, "Don't set the axis property to null."); if (newValue != null) { newValue.RegisteredListeners.Add(this); } if (oldValue != null) { oldValue.RegisteredListeners.Remove(this); } } #endregion public DisplayAxis Axis /// /// Instantiates a new instance of the DisplayAxisGridLines class. /// /// The axis used by the DisplayAxisGridLines. public DisplayAxisGridLines(DisplayAxis axis) { this.Axis = axis; this.SizeChanged += new SizeChangedEventHandler(OnSizeChanged); } /// /// Redraws grid lines when the size of the control changes. /// /// The source of the event. /// Information about the event. private void OnSizeChanged(object sender, SizeChangedEventArgs e) { Invalidate(); } /// /// Redraws grid lines when the axis is invalidated. /// /// The invalidated axis. public void AxisInvalidated(IAxis axis) { Invalidate(); } /// /// Draws the grid lines. /// protected abstract void Invalidate(); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IAnchoredToOrigin.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Range axes look for this interface on series to determine whether to /// anchor the origin to the bottom or top of the screen where possible. /// /// /// Implementing this interface ensures that value margins will not cause /// an origin to float above the bottom or top of the screen if no /// data exists below or above. /// public interface IAnchoredToOrigin { /// /// Gets the axis to which the data is anchored. /// IRangeAxis AnchoredAxis { get; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.ObjectModel; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis interface used to determine the plot area coordinate of values. /// public interface IAxis { /// /// Gets or sets the orientation of the axis. /// AxisOrientation Orientation { get; set; } /// /// This event is raised when the Orientation property is changed. /// event RoutedPropertyChangedEventHandler OrientationChanged; /// /// Returns a value indicating whether the axis can plot a value. /// /// The value to plot. /// A value indicating whether the axis can plot a value. /// bool CanPlot(object value); /// /// The plot area coordinate of a value. /// /// The value for which to retrieve the plot area /// coordinate. /// The plot area coordinate. UnitValue GetPlotAreaCoordinate(object value); /// /// Gets the registered IAxisListeners. /// ObservableCollection RegisteredListeners { get; } /// /// Gets the collection of child axes. /// ObservableCollection DependentAxes { get; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IAxisListener.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// An object that listens for changes in an axis. /// public interface IAxisListener { /// /// This method is called when the axis is invalidated. /// /// The axis that has been invalidated. void AxisInvalidated(IAxis axis); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/ICategoryAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that is arranged by category. /// public interface ICategoryAxis : IAxis, IDataConsumer { /// /// Accepts a category and returns the coordinate range of that category /// on the axis. /// /// A category for which to retrieve the /// coordinate location. /// The coordinate range of the category on the axis. Range GetPlotAreaCoordinateRange(object category); /// /// Returns the category at a given coordinate. /// /// The plot are coordinate. /// The category at the given plot area coordinate. object GetCategoryAtPosition(UnitValue position); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IDataConsumer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An object that consumes data. /// public interface IDataConsumer { /// /// Supplies the consumer with data. /// /// The data provider. /// The data used by the consumer. void DataChanged(IDataProvider dataProvider, IEnumerable data); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IDataProvider.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Provides information to a category axis. /// public interface IDataProvider { /// /// Retrieves the data to be plotted on the axis. /// /// The axis to retrieve the data for. /// The data to plot on the axis. IEnumerable GetData(IDataConsumer axis); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IRangeAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Diagnostics.CodeAnalysis; using System.Windows; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis with a range. /// public interface IRangeAxis : IAxis, IRangeConsumer { /// /// Gets the range of values displayed on the axis. /// Range Range { get; } /// /// The plot area coordinate of a value. /// /// The position at which to retrieve the plot /// area coordinate. /// The plot area coordinate. IComparable GetValueAtPosition(UnitValue position); /// /// Gets the origin value on the axis. /// IComparable Origin { get; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IRangeConsumer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An object that consumes a range. /// public interface IRangeConsumer { /// /// Informs a range consumer that a provider's range has changed. /// /// The range provider. /// The range of data. void RangeChanged(IRangeProvider provider, Range range); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IRangeProvider.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Provides information to a RangeConsumer. /// public interface IRangeProvider { /// /// Returns the range of values. /// /// The range consumer requesting the data /// range. /// A data range. Range GetRange(IRangeConsumer rangeConsumer); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IValueMarginConsumer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Consumes value margins and uses them to lay out objects. /// public interface IValueMarginConsumer { /// /// Updates layout to accommodate for value margins. /// /// A value margin provider. /// A sequence of value margins. void ValueMarginsChanged(IValueMarginProvider provider, IEnumerable valueMargins); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/IValueMarginProvider.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Provides information about margins necessary for values. /// public interface IValueMarginProvider { /// /// Gets the margins required for values. /// /// The axis to retrieve the value margins /// for. /// The margins required for values. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does a substantial amount of work.")] IEnumerable GetValueMargins(IValueMarginConsumer consumer); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/LinearAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that displays numeric values. /// [StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MinorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(NumericAxisLabel))] [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] [TemplatePart(Name = AxisGridName, Type = typeof(Grid))] [TemplatePart(Name = AxisTitleName, Type = typeof(Title))] public class LinearAxis : NumericAxis { #region public double? Interval /// /// Gets or sets the axis interval. /// [TypeConverter(typeof(NullableConverter))] public double? Interval { get { return (double?)GetValue(IntervalProperty); } set { SetValue(IntervalProperty, value); } } /// /// Identifies the Interval dependency property. /// public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register( "Interval", typeof(double?), typeof(LinearAxis), new PropertyMetadata(null, OnIntervalPropertyChanged)); /// /// IntervalProperty property changed handler. /// /// LinearAxis that changed its Interval. /// Event arguments. private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LinearAxis source = (LinearAxis)d; source.OnIntervalPropertyChanged(); } /// /// IntervalProperty property changed handler. /// private void OnIntervalPropertyChanged() { OnInvalidated(new RoutedEventArgs()); } #endregion public double? Interval #region public double ActualInterval /// /// Gets the actual interval of the axis. /// public double ActualInterval { get { return (double)GetValue(ActualIntervalProperty); } private set { SetValue(ActualIntervalProperty, value); } } /// /// Identifies the ActualInterval dependency property. /// public static readonly DependencyProperty ActualIntervalProperty = DependencyProperty.Register( "ActualInterval", typeof(double), typeof(LinearAxis), new PropertyMetadata(double.NaN)); #endregion public double ActualInterval /// /// Instantiates a new instance of the LinearAxis class. /// public LinearAxis() { this.ActualRange = new Range(0.0, 1.0); } /// /// Gets the actual range of double values. /// protected Range ActualDoubleRange { get; private set; } /// /// Updates ActualDoubleRange when ActualRange changes. /// /// New ActualRange value. protected override void OnActualRangeChanged(Range range) { ActualDoubleRange = range.ToDoubleRange(); base.OnActualRangeChanged(range); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The length of axis. /// The plot area coordinate of a value. protected override UnitValue GetPlotAreaCoordinate(object value, double length) { return GetPlotAreaCoordinate(value, ActualDoubleRange, length); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The range of values. /// The length of axis. /// The plot area coordinate of a value. protected override UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length) { return GetPlotAreaCoordinate(value, currentRange.ToDoubleRange(), length); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The range of values. /// The length of axis. /// The plot area coordinate of a value. private static UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length) { if (currentRange.HasData) { double doubleValue = ValueHelper.ToDouble(value); double pixelLength = Math.Max(length - 1, 0); double rangelength = currentRange.Maximum - currentRange.Minimum; return new UnitValue((doubleValue - currentRange.Minimum) * (pixelLength / rangelength), Unit.Pixels); } return UnitValue.NaN(); } /// /// Returns the actual interval to use to determine which values are /// displayed in the axis. /// /// The available size. /// Actual interval to use to determine which values are /// displayed in the axis. /// protected virtual double CalculateActualInterval(Size availableSize) { if (Interval != null) { return Interval.Value; } // Adjust maximum interval count adjusted for current axis double adjustedMaximumIntervalsPer200Pixels = (Orientation == AxisOrientation.X ? 0.8 : 1.0) * MaximumAxisIntervalsPer200Pixels; // Calculate maximum interval count for current space double maximumIntervalCount = Math.Max(GetLength(availableSize) * adjustedMaximumIntervalsPer200Pixels / 200.0, 1.0); // Calculate range double range = ActualDoubleRange.Maximum - ActualDoubleRange.Minimum; // Calculate largest acceptable interval double bestInterval = range / maximumIntervalCount; // Calculate mimimum ideal interval (ideal => something that gives nice axis values) double minimumIdealInterval = Math.Pow(10, Math.Floor(Math.Log10(bestInterval))); // Walk the list of ideal multipliers foreach (int idealMultiplier in new int[] { 10, 5, 2, 1 }) { // Check the current ideal multiplier against the maximum count double currentIdealInterval = minimumIdealInterval * idealMultiplier; if (maximumIntervalCount < (range / currentIdealInterval)) { // Went too far, break out break; } // Update the best interval bestInterval = currentIdealInterval; } // Return best interval return bestInterval; } /// /// Returns a sequence of values to create major tick marks for. /// /// The available size. /// A sequence of values to create major tick marks for. /// protected override IEnumerable GetMajorTickMarkValues(Size availableSize) { return GetMajorValues(availableSize).CastWrapper(); } /// /// Returns a sequence of major axis values. /// /// The available size. /// A sequence of major axis values. /// private IEnumerable GetMajorValues(Size availableSize) { if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0) { yield break; } this.ActualInterval = CalculateActualInterval(availableSize); double startValue = AlignToInterval(ActualDoubleRange.Minimum, this.ActualInterval); if (startValue < ActualDoubleRange.Minimum) { startValue = AlignToInterval(ActualDoubleRange.Minimum + this.ActualInterval, this.ActualInterval); } double nextValue = startValue; for (int counter = 1; nextValue <= ActualDoubleRange.Maximum; counter++) { yield return nextValue; nextValue = startValue + (counter * this.ActualInterval); } } /// /// Returns a sequence of values to plot on the axis. /// /// The available size. /// A sequence of values to plot on the axis. protected override IEnumerable GetLabelValues(Size availableSize) { return GetMajorValues(availableSize).CastWrapper(); } /// /// Aligns a value to the provided interval value. The aligned value /// should always be smaller than or equal to than the provided value. /// /// The value to align to the interval. /// The interval to align to. /// The aligned value. private static double AlignToInterval(double value, double interval) { double typedInterval = (double)interval; double typedValue = (double)value; return ValueHelper.RemoveNoiseFromDoubleMath(ValueHelper.RemoveNoiseFromDoubleMath(Math.Floor(typedValue / typedInterval)) * typedInterval); } /// /// Returns the value range given a plot area coordinate. /// /// The plot area position. /// The value at that plot area coordinate. protected override IComparable GetValueAtPosition(UnitValue value) { if (ActualRange.HasData && ActualLength != 0.0) { if (value.Unit == Unit.Pixels) { double coordinate = value.Value; double rangelength = ActualDoubleRange.Maximum - ActualDoubleRange.Minimum; double output = ((coordinate * (rangelength / ActualLength)) + ActualDoubleRange.Minimum); return output; } else { throw new NotImplementedException(); } } return null; } /// /// Function that uses the mid point of all the data values /// in the value margins to convert a length into a range /// of data with the mid point as the center of that range. /// /// The mid point of the range. /// The length of the range. /// The range object. private static Range LengthToRange(double midPoint, double length) { double halfLength = length / 2.0; return new Range(midPoint - halfLength, midPoint + halfLength); } /// /// Overrides the actual range to ensure that it is never set to an /// empty range. /// /// The range to override. /// Returns the overridden range. protected override Range OverrideDataRange(Range range) { range = base.OverrideDataRange(range); if (!range.HasData) { return new Range(0.0, 1.0); } else if (ValueHelper.Compare(range.Minimum, range.Maximum) == 0) { Range outputRange = new Range((ValueHelper.ToDouble(range.Minimum)) - 1, (ValueHelper.ToDouble(range.Maximum)) + 1); return outputRange; } // ActualLength of 1.0 or less maps all points to the same coordinate if (range.HasData && this.ActualLength > 1.0) { bool isDataAnchoredToOrigin = false; IList valueMargins = new List(); foreach (IValueMarginProvider valueMarginProvider in this.RegisteredListeners.OfType()) { foreach (ValueMargin valueMargin in valueMarginProvider.GetValueMargins(this)) { IAnchoredToOrigin dataAnchoredToOrigin = valueMarginProvider as IAnchoredToOrigin; isDataAnchoredToOrigin = (dataAnchoredToOrigin != null && dataAnchoredToOrigin.AnchoredAxis == this); valueMargins.Add( new ValueMarginCoordinateAndOverlap { ValueMargin = valueMargin, }); } } if (valueMargins.Count > 0) { double maximumPixelMarginLength = valueMargins .Select(valueMargin => valueMargin.ValueMargin.LowMargin + valueMargin.ValueMargin.HighMargin) .MaxOrNullable().Value; // Requested margin is larger than the axis so give up // trying to find a range that will fit it. if (maximumPixelMarginLength > this.ActualLength) { return range; } Range originalRange = range.ToDoubleRange(); Range currentRange = range.ToDoubleRange(); // Ensure range is not empty. if (currentRange.Minimum == currentRange.Maximum) { currentRange = new Range(currentRange.Maximum - 1, currentRange.Maximum + 1); } // priming the loop double actualLength = this.ActualLength; ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin; ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin; UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); while (maxLeftOverlapValueMargin.LeftOverlap > 0 || maxRightOverlapValueMargin.RightOverlap > 0) { double unitOverPixels = currentRange.GetLength().Value / actualLength; double newMinimum = currentRange.Minimum - ((maxLeftOverlapValueMargin.LeftOverlap + 0.5) * unitOverPixels); double newMaximum = currentRange.Maximum + ((maxRightOverlapValueMargin.RightOverlap + 0.5) * unitOverPixels); currentRange = new Range(newMinimum, newMaximum); UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); } if (isDataAnchoredToOrigin) { if (originalRange.Minimum >= 0 && currentRange.Minimum < 0) { currentRange = new Range(0, currentRange.Maximum); } else if (originalRange.Maximum <= 0 && currentRange.Maximum > 0) { currentRange = new Range(currentRange.Minimum, 0); } } return currentRange.ToComparableRange(); } } return range; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/LogarithmicAxis.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that displays numeric values along a logarithmic range. /// [StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "MinorTickMarkStyle", StyleTargetType = typeof(Line))] [StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(NumericAxisLabel))] [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] [TemplatePart(Name = AxisGridName, Type = typeof(Grid))] [TemplatePart(Name = AxisTitleName, Type = typeof(Title))] public class LogarithmicAxis : NumericAxis { /// /// Instantiates a new instance of the LogarithmicAxis /// public LogarithmicAxis() { ActualRange = new Range(1.0, 2.0); } /// /// Gets the actual range of double values. /// protected Range ActualDoubleRange { get; private set; } /// /// Updates ActualDoubleRange when ActualRange changes. /// /// New ActualRange value. protected override void OnActualRangeChanged(Range range) { ActualDoubleRange = range.ToDoubleRange(); base.OnActualRangeChanged(range); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The range of values. /// The length of axis. /// The plot area coordinate of a value. protected override UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length) { return GetPlotAreaCoordinate(value, currentRange.ToDoubleRange(), length); } protected override UnitValue GetPlotAreaCoordinate(object value, double length) { return GetPlotAreaCoordinate(value, ActualDoubleRange, length); } /// /// Returns the plot area coordinate of a value. /// /// The value to plot. /// The range of values. /// The length of the axis. /// The plot area coordinate of the value. protected static UnitValue GetPlotAreaCoordinate(object value, Range range, double length) { if (value == null) { throw new ArgumentNullException("value"); } if (range.HasData) { double doubleValue = ValueHelper.ToDouble(value); Range actualDoubleRange = range; return new UnitValue ( length / Math.Log10(actualDoubleRange.Maximum / actualDoubleRange.Minimum) * Math.Log10(doubleValue / actualDoubleRange.Minimum), Unit.Pixels ); } return new UnitValue(); } /// /// Returns the value range given a plot area coordinate. /// /// The plot area position. /// The value at that plot area coordinate. protected override IComparable GetValueAtPosition(UnitValue value) { if (ActualRange.HasData && ActualLength != 0.0) { if (value.Unit == Unit.Pixels) { double coordinate = value.Value; Range actualDoubleRange = ActualRange.ToDoubleRange(); double output = Math.Pow ( 10, coordinate * Math.Log10(actualDoubleRange.Maximum / actualDoubleRange.Minimum) / ActualLength ) * actualDoubleRange.Minimum; return output; } else { throw new NotImplementedException(); } } return null; } /// /// Returns a sequence of values to create major tick marks for. /// /// The available size. /// A sequence of values to create major tick marks for. /// protected override IEnumerable GetMajorTickMarkValues(Size availableSize) { return GetMajorValues(availableSize).Cast(); } /// /// Returns a sequence of values to plot on the axis. /// /// The available size. /// A sequence of values to plot on the axis. protected override IEnumerable GetLabelValues(Size availableSize) { return GetMajorValues(availableSize).Cast(); } private string _majorValues = "125;250;500;1000;2000;4000;8000"; public string MajorValues { get { return _majorValues; } set { _majorValues = value; } } /// /// Returns a sequence of major axis values. /// /// The available size. /// A sequence of major axis values. /// private IEnumerable GetMajorValues(Size availableSize) { if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0) { yield break; } foreach (var c in MajorValues.Split(new []{';'})) { yield return double.Parse(c); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/NullableConverter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using ResxResources = System.Windows.Controls.DataVisualization.Properties.Resources; using System.ComponentModel; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Converts a string or base value to a value. /// /// The type should be value type. /// Preview public class NullableConverter : TypeConverter where T : struct { /// /// Returns whether the type converter can convert an object from the /// specified type to the type of this converter. /// /// An object that provides a format context. /// /// The type you want to convert from. /// /// Returns true if this converter can perform the conversion; /// otherwise, false. /// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(T)) { return true; } else if (sourceType == typeof(string)) { return true; } return false; } /// /// Returns whether the type converter can convert an object from the /// specified type to the type of this converter. /// /// An object that provides a format context. /// /// The type you want to convert to. /// /// /// Returns true if this converter can perform the conversion; /// otherwise, false. /// public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return (destinationType == typeof(T)); } /// /// Converts from the specified value to the type of this converter. /// /// An object that provides a format context. /// /// The /// to use as the /// current culture. /// The value to convert to the type of this /// converter. /// The converted value. /// /// The conversion cannot be performed. /// public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { string stringValue = value as string; if (value is T) { return new Nullable((T)value); } else if (string.IsNullOrEmpty(stringValue) || String.Equals(stringValue, "Auto", StringComparison.OrdinalIgnoreCase)) { return new Nullable(); } return new Nullable((T)Convert.ChangeType(value, typeof(T), culture)); } /// /// Converts from the specified value to the a specified type from the /// type of this converter. /// /// An object that provides a format context. /// /// The /// to use as the /// current culture. /// The value to convert to the type of this /// converter. /// The type of convert the value to /// . /// The converted value. /// /// The conversion cannot be performed. /// public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value == null) { return string.Empty; } else if (destinationType == typeof(string)) { return value.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/NumericAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; using EF = System.Windows.Controls.DataVisualization.EnumerableFunctions; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that displays numeric values. /// public abstract class NumericAxis : RangeAxis { #region public double? ActualMaximum /// /// Gets the actual maximum value plotted on the chart. /// public double? ActualMaximum { get { return (double?)GetValue(ActualMaximumProperty); } private set { SetValue(ActualMaximumProperty, value); } } /// /// Identifies the ActualMaximum dependency property. /// public static readonly DependencyProperty ActualMaximumProperty = DependencyProperty.Register( "ActualMaximum", typeof(double?), typeof(NumericAxis), null); #endregion public double? ActualMaximum #region public double? ActualMinimum /// /// Gets the actual maximum value plotted on the chart. /// public double? ActualMinimum { get { return (double?)GetValue(ActualMinimumProperty); } private set { SetValue(ActualMinimumProperty, value); } } /// /// Identifies the ActualMinimum dependency property. /// public static readonly DependencyProperty ActualMinimumProperty = DependencyProperty.Register( "ActualMinimum", typeof(double?), typeof(NumericAxis), null); #endregion public double? ActualMinimum #region public double? Maximum /// /// Gets or sets the maximum value plotted on the axis. /// [TypeConverter(typeof(NullableConverter))] public double? Maximum { get { return (double?)GetValue(MaximumProperty); } set { SetValue(MaximumProperty, value); } } /// /// Identifies the Maximum dependency property. /// public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( "Maximum", typeof(double?), typeof(NumericAxis), new PropertyMetadata(null, OnMaximumPropertyChanged)); /// /// MaximumProperty property changed handler. /// /// NumericAxis that changed its Maximum. /// Event arguments. private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { NumericAxis source = (NumericAxis)d; double? newValue = (double?)e.NewValue; source.OnMaximumPropertyChanged(newValue); } /// /// MaximumProperty property changed handler. /// /// New value. protected virtual void OnMaximumPropertyChanged(double? newValue) { this.ProtectedMaximum = newValue; } #endregion public double? Maximum #region public double? Minimum /// /// Gets or sets the minimum value to plot on the axis. /// [TypeConverter(typeof(NullableConverter))] public double? Minimum { get { return (double?)GetValue(MinimumProperty); } set { SetValue(MinimumProperty, value); } } /// /// Identifies the Minimum dependency property. /// public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( "Minimum", typeof(double?), typeof(NumericAxis), new PropertyMetadata(null, OnMinimumPropertyChanged)); /// /// MinimumProperty property changed handler. /// /// NumericAxis that changed its Minimum. /// Event arguments. private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { NumericAxis source = (NumericAxis)d; double? newValue = (double?)e.NewValue; source.OnMinimumPropertyChanged(newValue); } /// /// MinimumProperty property changed handler. /// /// New value. protected virtual void OnMinimumPropertyChanged(double? newValue) { this.ProtectedMinimum = newValue; } #endregion public double? Minimum #region public bool ExtendRangeToOrigin /// /// Gets or sets a value indicating whether to always show the origin. /// public bool ExtendRangeToOrigin { get { return (bool)GetValue(ExtendRangeToOriginProperty); } set { SetValue(ExtendRangeToOriginProperty, value); } } /// /// Identifies the ExtendRangeToOrigin dependency property. /// public static readonly DependencyProperty ExtendRangeToOriginProperty = DependencyProperty.Register( "ExtendRangeToOrigin", typeof(bool), typeof(NumericAxis), new PropertyMetadata(false, OnExtendRangeToOriginPropertyChanged)); /// /// ExtendRangeToOriginProperty property changed handler. /// /// NumericAxis that changed its ExtendRangeToOrigin. /// Event arguments. private static void OnExtendRangeToOriginPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { NumericAxis source = (NumericAxis)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnExtendRangeToOriginPropertyChanged(oldValue, newValue); } /// /// ExtendRangeToOriginProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnExtendRangeToOriginPropertyChanged(bool oldValue, bool newValue) { this.ActualRange = this.OverrideDataRange(this.ActualRange); } #endregion public bool ExtendRangeToOrigin /// /// Gets the origin value on the axis. /// protected override IComparable Origin { get { return 0.0; } } /// /// Instantiates a new instance of the NumericAxis class. /// protected NumericAxis() { } /// /// Updates the typed actual maximum and minimum properties when the /// actual range changes. /// /// The actual range. protected override void OnActualRangeChanged(Range range) { if (range.HasData) { this.ActualMaximum = (double)range.Maximum; this.ActualMinimum = (double)range.Minimum; } else { this.ActualMaximum = null; this.ActualMinimum = null; } base.OnActualRangeChanged(range); } /// /// Returns a value indicating whether a value can plot. /// /// The value to plot. /// A value indicating whether a value can plot. public override bool CanPlot(object value) { double val; return ValueHelper.TryConvert(value, out val); } /// /// Returns a numeric axis label. /// /// A numeric axis label. protected override Control CreateAxisLabel() { return new NumericAxisLabel(); } /// /// Overrides the data value range and returns a range that takes the /// margins of the values into account. /// /// The range of data values. /// A range that can store both the data values and their /// margins. protected override Range OverrideDataRange(Range range) { range = base.OverrideDataRange(range); if (ExtendRangeToOrigin) { Range adjustedRange = range.ToDoubleRange(); if (!adjustedRange.HasData) { return new Range(0.0, 0.0); } else { double minimum = adjustedRange.Minimum; double maximum = adjustedRange.Maximum; if (minimum > 0.0) { minimum = 0.0; } else if (maximum < 0.0) { maximum = 0.0; } return new Range(minimum, maximum); } } return range; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/NumericAxisLabel.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Windows; namespace System.Windows.Controls.DataVisualization.Charting { /// /// A label used to display numeric axis values. /// public class NumericAxisLabel : AxisLabel { /// /// Initializes the static members of the NumericAxisLabel class. /// static NumericAxisLabel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericAxisLabel), new FrameworkPropertyMetadata(typeof(NumericAxisLabel))); } /// /// Instantiates a new instance of the NumericAxisLabel class. /// public NumericAxisLabel() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/OrientedAxisGridLines.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// This control draws gridlines with the help of an axis. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected capitalization.")] internal class OrientedAxisGridLines : DisplayAxisGridLines { /// /// A pool of grid lines. /// private ObjectPool _gridLinePool; /// /// Initializes a new instance of the OrientedAxisGridLines class. /// /// The axis to draw grid lines for. public OrientedAxisGridLines(DisplayAxis displayAxis) : base(displayAxis) { _gridLinePool = new ObjectPool(() => new Line { Style = Axis.GridLineStyle }); } /// /// Draws the grid lines. /// protected override void Invalidate() { _gridLinePool.Reset(); try { IList intervals = Axis.InternalGetMajorGridLinePositions().ToList(); this.Children.Clear(); double maximumHeight = Math.Max(Math.Round(ActualHeight - 1), 0); double maximumWidth = Math.Max(Math.Round(ActualWidth - 1), 0); for (int index = 0; index < intervals.Count; index++) { double currentValue = intervals[index].Value; double position = currentValue; if (!double.IsNaN(position)) { Line line = _gridLinePool.Next(); if (Axis.Orientation == AxisOrientation.Y) { line.Y1 = line.Y2 = maximumHeight - Math.Round(position - (line.StrokeThickness / 2)); line.X1 = 0.0; line.X2 = maximumWidth; } else if (Axis.Orientation == AxisOrientation.X) { line.X1 = line.X2 = Math.Round(position - (line.StrokeThickness / 2)); line.Y1 = 0.0; line.Y2 = maximumHeight; } // workaround for '1px line thickness issue' if (line.StrokeThickness % 2 > 0) { line.SetValue(Canvas.LeftProperty, 0.5); line.SetValue(Canvas.TopProperty, 0.5); } this.Children.Add(line); } } } finally { _gridLinePool.Done(); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/RangeAxis.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Media; using System.Windows.Controls; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axis that has a range. /// public abstract class RangeAxis : DisplayAxis, IRangeAxis, IValueMarginConsumer { /// /// A pool of major tick marks. /// private ObjectPool _majorTickMarkPool; /// /// A pool of major tick marks. /// private ObjectPool _minorTickMarkPool; /// /// A pool of labels. /// private ObjectPool _labelPool; #region public Style MinorTickMarkStyle /// /// Gets or sets the minor tick mark style. /// public Style MinorTickMarkStyle { get { return GetValue(MinorTickMarkStyleProperty) as Style; } set { SetValue(MinorTickMarkStyleProperty, value); } } /// /// Identifies the MinorTickMarkStyle dependency property. /// public static readonly DependencyProperty MinorTickMarkStyleProperty = DependencyProperty.Register( "MinorTickMarkStyle", typeof(Style), typeof(RangeAxis), new PropertyMetadata(null)); #endregion public Style MinorTickMarkStyle /// /// The actual range of values. /// private Range _actualRange; /// /// Gets or sets the actual range of values. /// protected Range ActualRange { get { return _actualRange; } set { Range oldValue = _actualRange; Range minMaxEnforcedValue = EnforceMaximumAndMinimum(value); if (!oldValue.Equals(minMaxEnforcedValue)) { _actualRange = minMaxEnforcedValue; OnActualRangeChanged(minMaxEnforcedValue); } } } /// /// The maximum value displayed in the range axis. /// private IComparable _protectedMaximum; /// /// Gets or sets the maximum value displayed in the range axis. /// protected IComparable ProtectedMaximum { get { return _protectedMaximum; } set { if (value != null && ProtectedMinimum != null && ValueHelper.Compare(ProtectedMinimum, value) > 0) { throw new InvalidOperationException(Properties.Resources.RangeAxis_MaximumValueMustBeLargerThanOrEqualToMinimumValue); } if (!object.ReferenceEquals(_protectedMaximum, value) && !object.Equals(_protectedMaximum, value)) { _protectedMaximum = value; UpdateActualRange(); } } } /// /// The minimum value displayed in the range axis. /// private IComparable _protectedMinimum; /// /// Gets or sets the minimum value displayed in the range axis. /// protected IComparable ProtectedMinimum { get { return _protectedMinimum; } set { if (value != null && ProtectedMaximum != null && ValueHelper.Compare(value, ProtectedMaximum) > 0) { throw new InvalidOperationException(Properties.Resources.RangeAxis_MinimumValueMustBeLargerThanOrEqualToMaximumValue); } if (!object.ReferenceEquals(_protectedMinimum, value) && !object.Equals(_protectedMinimum, value)) { _protectedMinimum = value; UpdateActualRange(); } } } /// /// Initializes the static members of the RangeAxis class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static RangeAxis() { DefaultStyleKeyProperty.OverrideMetadata(typeof(RangeAxis), new FrameworkPropertyMetadata(typeof(RangeAxis))); } /// /// Instantiates a new instance of the RangeAxis class. /// protected RangeAxis() { this._labelPool = new ObjectPool(() => CreateAxisLabel()); this._majorTickMarkPool = new ObjectPool(() => CreateMajorTickMark()); this._minorTickMarkPool = new ObjectPool(() => CreateMinorTickMark()); // Update actual range when size changes for the first time. This // is necessary because the value margins may have changed after // the first layout pass. SizeChangedEventHandler handler = null; handler = delegate { SizeChanged -= handler; UpdateActualRange(); }; SizeChanged += handler; } /// /// Creates a minor axis tick mark. /// /// A line to used to render a tick mark. protected Line CreateMinorTickMark() { return CreateTickMark(MinorTickMarkStyle); } /// /// Invalidates axis when the actual range changes. /// /// The new actual range. protected virtual void OnActualRangeChanged(Range range) { Invalidate(); } /// /// Returns the plot area coordinate of a given value. /// /// The value to return the plot area coordinate for. /// The plot area coordinate of the given value. public override UnitValue GetPlotAreaCoordinate(object value) { if (value == null) { throw new ArgumentNullException("value"); } return GetPlotAreaCoordinate(value, ActualLength); } /// /// Returns the plot area coordinate of a given value. /// /// The value to return the plot area coordinate for. /// The length of the axis. /// The plot area coordinate of the given value. protected abstract UnitValue GetPlotAreaCoordinate(object value, double length); /// /// Returns the plot area coordinate of a given value. /// /// The value to return the plot area coordinate for. /// The value range to use when calculating the plot area coordinate. /// The length of the axis. /// The plot area coordinate of the given value. protected abstract UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length); /// /// Overrides the data range. /// /// The range to potentially override. /// The overridden range. protected virtual Range OverrideDataRange(Range range) { return range; } /// /// Modifies a range to respect the minimum and maximum axis values. /// /// The range of data. /// A range modified to respect the minimum and maximum axis /// values. private Range EnforceMaximumAndMinimum(Range range) { if (range.HasData) { IComparable minimum = ProtectedMinimum ?? range.Minimum; IComparable maximum = ProtectedMaximum ?? range.Maximum; if (ValueHelper.Compare(minimum, maximum) > 0) { IComparable temp = maximum; maximum = minimum; minimum = temp; } return new Range(minimum, maximum); } else { IComparable minimum = ProtectedMinimum; IComparable maximum = ProtectedMaximum; if (ProtectedMinimum != null && ProtectedMaximum == null) { maximum = minimum; } else if (ProtectedMaximum != null && ProtectedMinimum == null) { minimum = maximum; } else { return range; } return new Range(minimum, maximum); } } /// /// Updates the actual range displayed on the axis. /// private void UpdateActualRange() { Action action = () => { Range dataRange; if (ProtectedMaximum == null || ProtectedMinimum == null) { if (Orientation == AxisOrientation.None) { if (ProtectedMinimum != null) { this.ActualRange = OverrideDataRange(new Range(ProtectedMinimum, ProtectedMinimum)); } else { this.ActualRange = OverrideDataRange(new Range(ProtectedMaximum, ProtectedMaximum)); } } else { dataRange = this.RegisteredListeners .OfType() .Select(rangeProvider => rangeProvider.GetRange(this)) .Sum(); this.ActualRange = OverrideDataRange(dataRange); } } else { this.ActualRange = new Range(ProtectedMinimum, ProtectedMaximum); } }; // Repeat this after layout pass. if (this.ActualLength == 0.0) { this.Dispatcher.BeginInvoke(action); } action(); } /// /// Renders the axis as an oriented axis. /// /// The available size. private void RenderOriented(Size availableSize) { _minorTickMarkPool.Reset(); _majorTickMarkPool.Reset(); _labelPool.Reset(); double length = GetLength(availableSize); try { OrientedPanel.Children.Clear(); if (ActualRange.HasData && !Object.Equals(ActualRange.Minimum, ActualRange.Maximum)) { foreach (IComparable axisValue in GetMajorTickMarkValues(availableSize)) { UnitValue coordinate = GetPlotAreaCoordinate(axisValue, length); if (ValueHelper.CanGraph(coordinate.Value)) { Line line = _majorTickMarkPool.Next(); OrientedPanel.SetCenterCoordinate(line, coordinate.Value); OrientedPanel.SetPriority(line, 0); OrientedPanel.Children.Add(line); } } foreach (IComparable axisValue in GetMinorTickMarkValues(availableSize)) { UnitValue coordinate = GetPlotAreaCoordinate(axisValue, length); if (ValueHelper.CanGraph(coordinate.Value)) { Line line = _minorTickMarkPool.Next(); OrientedPanel.SetCenterCoordinate(line, coordinate.Value); OrientedPanel.SetPriority(line, 0); OrientedPanel.Children.Add(line); } } int count = 0; foreach (IComparable axisValue in GetLabelValues(availableSize)) { UnitValue coordinate = GetPlotAreaCoordinate(axisValue, length); if (ValueHelper.CanGraph(coordinate.Value)) { Control axisLabel = _labelPool.Next(); PrepareAxisLabel(axisLabel, axisValue); OrientedPanel.SetCenterCoordinate(axisLabel, coordinate.Value); OrientedPanel.SetPriority(axisLabel, count + 1); OrientedPanel.Children.Add(axisLabel); count = (count + 1) % 2; } } } } finally { _minorTickMarkPool.Done(); _majorTickMarkPool.Done(); _labelPool.Done(); } } /// /// Renders the axis labels, tick marks, and other visual elements. /// /// The available size. protected override void Render(Size availableSize) { RenderOriented(availableSize); } /// /// Returns a sequence of the major grid line coordinates. /// /// The available size. /// A sequence of the major grid line coordinates. protected override IEnumerable GetMajorGridLineCoordinates(Size availableSize) { return GetMajorTickMarkValues(availableSize).Select(value => GetPlotAreaCoordinate(value)).Where(value => ValueHelper.CanGraph(value.Value)); } /// /// Returns a sequence of the values at which to plot major grid lines. /// /// The available size. /// A sequence of the values at which to plot major grid lines. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] protected virtual IEnumerable GetMajorGridLineValues(Size availableSize) { return GetMajorTickMarkValues(availableSize); } /// /// Returns a sequence of values to plot on the axis. /// /// The available size. /// A sequence of values to plot on the axis. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] protected abstract IEnumerable GetMajorTickMarkValues(Size availableSize); /// /// Returns a sequence of values to plot on the axis. /// /// The available size. /// A sequence of values to plot on the axis. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] protected virtual IEnumerable GetMinorTickMarkValues(Size availableSize) { yield break; } /// /// Returns a sequence of values to plot on the axis. /// /// The available size. /// A sequence of values to plot on the axis. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] protected abstract IEnumerable GetLabelValues(Size availableSize); /// /// Returns the value range given a plot area coordinate. /// /// The plot area coordinate. /// A range of values at that plot area coordinate. protected abstract IComparable GetValueAtPosition(UnitValue value); /// /// Gets the actual maximum value. /// Range IRangeAxis.Range { get { return ActualRange; } } /// /// Returns the value range given a plot area coordinate. /// /// The plot area coordinate. /// A range of values at that plot area coordinate. IComparable IRangeAxis.GetValueAtPosition(UnitValue value) { return GetValueAtPosition(value); } /// /// Updates the axis with information about a provider's data range. /// /// The information provider. /// The range of data in the information provider. /// void IRangeConsumer.RangeChanged(IRangeProvider usesRangeAxis, Range range) { UpdateActualRange(); } /// /// Updates the layout of the axis to accommodate a sequence of value /// margins. /// /// A value margin provider. /// A sequence of value margins. void IValueMarginConsumer.ValueMarginsChanged(IValueMarginProvider provider, IEnumerable valueMargins) { Action action = () => { if (this.Orientation != AxisOrientation.None) { // Determine if any of the value margins are outside the axis // area. If so update range. bool updateRange = valueMargins .Select( valueMargin => { double coordinate = GetPlotAreaCoordinate(valueMargin.Value).Value; return new Range(coordinate - valueMargin.LowMargin, coordinate + valueMargin.HighMargin); }) .Where(range => range.Minimum < 0 || range.Maximum > this.ActualLength) .Any(); if (updateRange) { UpdateActualRange(); } } }; // Repeat this after layout pass. if (this.ActualLength == 0) { this.Dispatcher.BeginInvoke(action); } else { action(); } } /// /// If a new range provider is registered, update actual range. /// /// The axis listener being registered. protected override void OnObjectRegistered(IAxisListener series) { base.OnObjectRegistered(series); if (series is IRangeProvider || series is IValueMarginProvider) { UpdateActualRange(); } } /// /// If a range provider is unregistered, update actual range. /// /// The axis listener being unregistered. protected override void OnObjectUnregistered(IAxisListener series) { base.OnObjectUnregistered(series); if (series is IRangeProvider || series is IValueMarginProvider) { UpdateActualRange(); } } /// /// Create function that when given a range will return the /// amount in pixels by which the value margin range /// overlaps. Positive numbers represent values outside the /// range. /// /// The list of value margins, coordinates, and overlaps. /// The new range to use to calculate coordinates. internal void UpdateValueMargins(IList valueMargins, Range comparableRange) { double actualLength = this.ActualLength; int valueMarginsCount = valueMargins.Count; for (int count = 0; count < valueMarginsCount; count++) { ValueMarginCoordinateAndOverlap item = valueMargins[count]; item.Coordinate = GetPlotAreaCoordinate(item.ValueMargin.Value, comparableRange, actualLength).Value; item.LeftOverlap = -(item.Coordinate - item.ValueMargin.LowMargin); item.RightOverlap = (item.Coordinate + item.ValueMargin.HighMargin) - actualLength; } } /// /// Returns the value margin, coordinate, and overlap triples that have the largest left and right overlap. /// /// The list of value margin, coordinate, and /// overlap triples. /// The value margin, /// coordinate, and overlap triple that has the largest left overlap. /// /// The value margin, /// coordinate, and overlap triple that has the largest right overlap. /// internal static void GetMaxLeftAndRightOverlap(IList valueMargins, out ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin, out ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin) { maxLeftOverlapValueMargin = new ValueMarginCoordinateAndOverlap(); maxRightOverlapValueMargin = new ValueMarginCoordinateAndOverlap(); double maxLeftOverlap = double.MinValue; double maxRightOverlap = double.MinValue; int valueMarginsCount = valueMargins.Count; for (int cnt = 0; cnt < valueMarginsCount; cnt++) { ValueMarginCoordinateAndOverlap valueMargin = valueMargins[cnt]; double leftOverlap = valueMargin.LeftOverlap; if (leftOverlap > maxLeftOverlap) { maxLeftOverlap = leftOverlap; maxLeftOverlapValueMargin = valueMargin; } double rightOverlap = valueMargin.RightOverlap; if (rightOverlap > maxRightOverlap) { maxRightOverlap = rightOverlap; maxRightOverlapValueMargin = valueMargin; } } } /// /// Gets the origin value on the axis. /// IComparable IRangeAxis.Origin { get { return this.Origin; } } /// /// Gets the origin value on the axis. /// protected abstract IComparable Origin { get; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Axis/ValueMargin.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// A margin specified for a given value. /// public struct ValueMargin { /// /// Gets the value that the margin is associated with. /// public object Value { get; private set; } /// /// Gets the low margin for a value. /// public double LowMargin { get; private set; } /// /// Gets the high margin for a value. /// public double HighMargin { get; private set; } /// /// Initializes a new instance of the ValueMargin class. /// /// The value the margin is associated with. /// The lower margin. /// The higher margin. public ValueMargin(object value, double lowMargin, double highMargin) : this() { Value = value; LowMargin = lowMargin; HighMargin = highMargin; } /// /// Determines whether two value margins are equal. /// /// The value margin to compare with this one. /// A value indicating whether the two value margins are equal. /// public override bool Equals(object obj) { if (obj is ValueMargin) { ValueMargin valueMargin = (ValueMargin)obj; return this.Value.Equals(valueMargin.Value) && this.LowMargin.Equals(valueMargin.LowMargin) && this.HighMargin.Equals(valueMargin.HighMargin); } return false; } /// /// Determines whether two unit value objects are equal. /// /// The left value margin. /// The right value margin. /// A value indicating whether two value margins objects are /// equal. public static bool operator ==(ValueMargin left, ValueMargin right) { return left.Equals(right); } /// /// Determines whether two value margin objects are not equal. /// /// The left value margin. /// The right value margin. /// A value indicating whether two value margin objects are not /// equal. public static bool operator !=(ValueMargin left, ValueMargin right) { return !left.Equals(right); } /// /// Returns the hash code of the value margin object. /// /// The hash code. public override int GetHashCode() { return this.Value.GetHashCode() ^ this.LowMargin.GetHashCode() ^ this.HighMargin.GetHashCode(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Chart/Chart.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Controls.DataVisualization.Charting.Primitives; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that displays a Chart. /// /// Preview [TemplatePart(Name = Chart.ChartAreaName, Type = typeof(EdgePanel))] [TemplatePart(Name = Chart.LegendName, Type = typeof(Legend))] [TemplatePart(Name = Chart.SelectionAreaName, Type = typeof(Canvas))] [TemplatePart(Name = Chart.PlotAreaName, Type = typeof(Grid))] [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] [StyleTypedProperty(Property = "LegendStyle", StyleTargetType = typeof(Legend))] [StyleTypedProperty(Property = "ChartAreaStyle", StyleTargetType = typeof(EdgePanel))] [StyleTypedProperty(Property = "PlotAreaStyle", StyleTargetType = typeof(Grid))] [ContentProperty("Series")] public partial class Chart : Control, ISeriesHost { /// /// Specifies the name of the ChartArea TemplatePart. /// private const string ChartAreaName = "ChartArea"; /// /// Specifies the name of the ChartArea TemplatePart. /// private const string SelectionAreaName = "SelectionArea"; private const string PlotAreaName = "PlotArea"; /// /// Specifies the name of the legend TemplatePart. /// private const string LegendName = "Legend"; /// /// Specifies the name of the legend TemplatePart. /// private const string CrosshairContainerName = "PART_CrosshairContainer"; /// /// Gets or sets the chart area children collection. /// private AggregatedObservableCollection ChartAreaChildren { get; set; } /// /// An adapter that synchronizes changes to the ChartAreaChildren /// property to the ChartArea panel's children collection. /// private ObservableCollectionListAdapter _chartAreaChildrenListAdapter = new ObservableCollectionListAdapter(); /// /// Gets or sets a collection of Axes in the Chart. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] public Collection Axes { get { return _axes; } set { throw new NotSupportedException(Properties.Resources.Chart_Axes_SetterNotSupported); } } /// /// Stores the collection of Axes in the Chart. /// private Collection _axes; /// /// The collection of foreground elements. /// private ObservableCollection _foregroundElements = new NoResetObservableCollection(); /// /// The collection of background elements. /// private ObservableCollection _backgroundElements = new NoResetObservableCollection(); /// /// Gets the collection of foreground elements. /// ObservableCollection ISeriesHost.ForegroundElements { get { return ForegroundElements; } } /// /// Gets the collection of foreground elements. /// protected ObservableCollection ForegroundElements { get { return _foregroundElements; } } /// /// Gets the collection of background elements. /// ObservableCollection ISeriesHost.BackgroundElements { get { return BackgroundElements; } } /// /// Gets the collection of background elements. /// protected ObservableCollection BackgroundElements { get { return _backgroundElements; } } /// /// Axes arranged along the edges. /// private ObservableCollection _edgeAxes = new NoResetObservableCollection(); /// /// Gets or sets the axes that are currently in the chart. /// private IList InternalActualAxes { get; set; } /// /// Gets the actual axes displayed in the chart. /// public ReadOnlyCollection ActualAxes { get; private set; } /// /// Gets or sets the reference to the template's ChartArea. /// private EdgePanel ChartArea { get; set; } /// /// Gets or sets the reference to the Chart's Legend. /// private Legend Legend { get; set; } private Canvas SelectionArea { get; set; } private Grid PlotArea { get; set; } private Grid CrosshairContainer { get; set; } private Grid Crosshair { get; set; } private Border LocationIndicator { get; set; } /// /// Gets or sets the collection of Series displayed by the Chart. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] public Collection Series { get { return _series; } set { throw new NotSupportedException(Properties.Resources.Chart_Series_SetterNotSupported); } } /// /// Stores the collection of Series displayed by the Chart. /// private Collection _series; #region ItemsSource /// /// List of CLR-objects which represent series of the chart /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(Chart), new PropertyMetadata(null, (s, e) => ((Chart)s).InitSeries())); /// /// Template for an item, transforms a CLR-object to an ISeries object /// public DataTemplate ItemTemplate { get { return (DataTemplate)GetValue(ItemTemplateProperty); } set { SetValue(ItemTemplateProperty, value); } } public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(Chart), new PropertyMetadata(null, (s, e) => ((Chart)s).InitSeries())); /// /// This property is necessary for stacked charts /// public DataTemplate ItemsHostTemplate { get { return (DataTemplate)GetValue(ItemsHostTemplateProperty); } set { SetValue(ItemsHostTemplateProperty, value); } } public static readonly DependencyProperty ItemsHostTemplateProperty = DependencyProperty.Register("ItemsHostTemplate", typeof(DataTemplate), typeof(Chart), new PropertyMetadata(null, (s, e) => ((Chart)s).InitSeries())); private void InitSeries() { this.Series.Clear(); if (this.ItemsSource == null || this.ItemTemplate == null) return; //From items to series var series = from item in this.ItemsSource.OfType() let seriesItem = this.ItemTemplate.LoadContent() as ISeries where seriesItem != null && seriesItem is FrameworkElement let dummy = ((FrameworkElement)seriesItem).DataContext = item select seriesItem; //Generic series and stacked series are different, that's why I use this if-else var hostSeries = this.ItemsHostTemplate != null ? this.ItemsHostTemplate.LoadContent() as DefinitionSeries : null; if (hostSeries != null) { this.Series.Add(hostSeries); series.OfType().ToList().ForEach(hostSeries.SeriesDefinitions.Add); } else series.ToList().ForEach(this.Series.Add); } #endregion #region public Style ChartAreaStyle /// /// Gets or sets the Style of the ISeriesHost's ChartArea. /// public Style ChartAreaStyle { get { return GetValue(ChartAreaStyleProperty) as Style; } set { SetValue(ChartAreaStyleProperty, value); } } /// /// Identifies the ChartAreaStyle dependency property. /// public static readonly DependencyProperty ChartAreaStyleProperty = DependencyProperty.Register( "ChartAreaStyle", typeof(Style), typeof(Chart), null); #endregion public Style ChartAreaStyle /// /// Gets the collection of legend items. /// public Collection LegendItems { get; private set; } #region public Style LegendStyle /// /// Gets or sets the Style of the ISeriesHost's Legend. /// public Style LegendStyle { get { return GetValue(LegendStyleProperty) as Style; } set { SetValue(LegendStyleProperty, value); } } /// /// Identifies the LegendStyle dependency property. /// public static readonly DependencyProperty LegendStyleProperty = DependencyProperty.Register( "LegendStyle", typeof(Style), typeof(Chart), null); #endregion public Style LegendStyle #region public object LegendTitle /// /// Gets or sets the Title content of the Legend. /// public object LegendTitle { get { return GetValue(LegendTitleProperty); } set { SetValue(LegendTitleProperty, value); } } /// /// Identifies the LegendTitle dependency property. /// public static readonly DependencyProperty LegendTitleProperty = DependencyProperty.Register( "LegendTitle", typeof(object), typeof(Chart), null); #endregion public object LegendTitle #region public Style PlotAreaStyle /// /// Gets or sets the Style of the ISeriesHost's PlotArea. /// public Style PlotAreaStyle { get { return GetValue(PlotAreaStyleProperty) as Style; } set { SetValue(PlotAreaStyleProperty, value); } } /// /// Identifies the PlotAreaStyle dependency property. /// public static readonly DependencyProperty PlotAreaStyleProperty = DependencyProperty.Register( "PlotAreaStyle", typeof(Style), typeof(Chart), null); #endregion public Style PlotAreaStyle #region public Collection Palette /// /// Gets or sets a palette of ResourceDictionaries used by the children of the Chart. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Want to allow this to be set from XAML.")] public Collection Palette { get { return GetValue(PaletteProperty) as Collection; } set { SetValue(PaletteProperty, value); } } /// /// Identifies the Palette dependency property. /// public static readonly DependencyProperty PaletteProperty = DependencyProperty.Register( "Palette", typeof(Collection), typeof(Chart), new PropertyMetadata(OnPalettePropertyChanged)); /// /// Called when the value of the Palette property is changed. /// /// Chart that contains the changed Palette. /// /// Event arguments. private static void OnPalettePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart source = (Chart) d; Collection newValue = (Collection)e.NewValue; source.OnPalettePropertyChanged(newValue); } /// /// Called when the value of the Palette property is changed. /// /// The new value for the Palette. private void OnPalettePropertyChanged(Collection newValue) { ResourceDictionaryDispenser.ResourceDictionaries = newValue; } #endregion public Collection Palette public Visibility CrosshairVisibility { get { return (Visibility)GetValue(CrosshairVisibilityProperty); } set { SetValue(CrosshairVisibilityProperty, value); } } public static readonly DependencyProperty CrosshairVisibilityProperty = DependencyProperty.Register("CrosshairVisibility", typeof(Visibility), typeof(Chart), new PropertyMetadata(Visibility.Collapsed)); /// /// Gets or sets an object that rotates through the palette. /// private ResourceDictionaryDispenser ResourceDictionaryDispenser { get; set; } /// /// Event that is invoked when the ResourceDictionaryDispenser's collection has changed. /// public event EventHandler ResourceDictionariesChanged; #region public object Title /// /// Gets or sets the title displayed for the Chart. /// public object Title { get { return GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof(object), typeof(Chart), null); #endregion #region public Style TitleStyle /// /// Gets or sets the Style of the ISeriesHost's Title. /// public Style TitleStyle { get { return GetValue(TitleStyleProperty) as Style; } set { SetValue(TitleStyleProperty, value); } } /// /// Identifies the TitleStyle dependency property. /// public static readonly DependencyProperty TitleStyleProperty = DependencyProperty.Register( "TitleStyle", typeof(Style), typeof(Chart), null); #endregion public Style TitleStyle /// /// Initializes the static members of the Chart class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static Chart() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Chart), new FrameworkPropertyMetadata(typeof(Chart))); } /// /// Initializes a new instance of the Chart class. /// public Chart() { // Create the backing collection for Series UniqueObservableCollection series = new UniqueObservableCollection(); series.CollectionChanged += new NotifyCollectionChangedEventHandler(SeriesCollectionChanged); _series = series; // Create the backing collection for Axes UniqueObservableCollection axes = new UniqueObservableCollection(); _axes = axes; ObservableCollection actualAxes = new SeriesHostAxesCollection(this, axes); actualAxes.CollectionChanged += ActualAxesCollectionChanged; this.InternalActualAxes = actualAxes; this.ActualAxes = new ReadOnlyCollection(InternalActualAxes); // Create collection for LegendItems LegendItems = new AggregatedObservableCollection(); ChartAreaChildren = new AggregatedObservableCollection(); ChartAreaChildren.ChildCollections.Add(_edgeAxes); ChartAreaChildren.ChildCollections.Add(_backgroundElements); ChartAreaChildren.ChildCollections.Add(Series); ChartAreaChildren.ChildCollections.Add(_foregroundElements); _chartAreaChildrenListAdapter.Collection = ChartAreaChildren; // Create a dispenser ResourceDictionaryDispenser = new ResourceDictionaryDispenser(); ResourceDictionaryDispenser.ResourceDictionariesChanged += delegate { OnResourceDictionariesChanged(EventArgs.Empty); }; } /// /// Invokes the ResourceDictionariesChanged event. /// /// Event arguments. private void OnResourceDictionariesChanged(EventArgs e) { // Forward event on to listeners EventHandler handler = ResourceDictionariesChanged; if (null != handler) { handler.Invoke(this, e); } } /// /// Determines the location of an axis based on the existing axes in /// the chart. /// /// The axis to determine the location of. /// The location of the axis. private AxisLocation GetAutoAxisLocation(Axis axis) { if (axis.Orientation == AxisOrientation.X) { int numberOfTopAxes = InternalActualAxes.OfType().Where(currentAxis => currentAxis.Location == AxisLocation.Top).Count(); int numberOfBottomAxes = InternalActualAxes.OfType().Where(currentAxis => currentAxis.Location == AxisLocation.Bottom).Count(); return (numberOfBottomAxes > numberOfTopAxes) ? AxisLocation.Top : AxisLocation.Bottom; } else if (axis.Orientation == AxisOrientation.Y) { int numberOfLeftAxes = InternalActualAxes.OfType().Where(currentAxis => currentAxis.Location == AxisLocation.Left).Count(); int numberOfRightAxes = InternalActualAxes.OfType().Where(currentAxis => currentAxis.Location == AxisLocation.Right).Count(); return (numberOfLeftAxes > numberOfRightAxes) ? AxisLocation.Right : AxisLocation.Left; } else { return AxisLocation.Auto; } } /// /// Adds an axis to the ISeriesHost area. /// /// The axis to add to the ISeriesHost area. private void AddAxisToChartArea(Axis axis) { IRequireSeriesHost requiresSeriesHost = axis as IRequireSeriesHost; if (requiresSeriesHost != null) { requiresSeriesHost.SeriesHost = this; } if (axis.Location == AxisLocation.Auto) { axis.Location = GetAutoAxisLocation(axis); } SetEdge(axis); axis.LocationChanged += AxisLocationChanged; axis.OrientationChanged += AxisOrientationChanged; if (axis.Location != AxisLocation.Auto) { _edgeAxes.Add(axis); } } /// /// Rebuilds the chart area if an axis orientation is changed. /// /// The source of the event. /// Information about the event. private void AxisOrientationChanged(object sender, RoutedPropertyChangedEventArgs args) { Axis axis = (Axis)sender; axis.Location = GetAutoAxisLocation(axis); } /// /// Sets the Edge property of an axis based on its location and /// orientation. /// /// The axis to set the edge property of. private static void SetEdge(Axis axis) { switch (axis.Location) { case AxisLocation.Bottom: EdgePanel.SetEdge(axis, Edge.Bottom); break; case AxisLocation.Top: EdgePanel.SetEdge(axis, Edge.Top); break; case AxisLocation.Left: EdgePanel.SetEdge(axis, Edge.Left); break; case AxisLocation.Right: EdgePanel.SetEdge(axis, Edge.Right); break; } } /// /// Rebuild the chart area if an axis location is changed. /// /// The source of the event. /// Information about the event. private void AxisLocationChanged(object sender, RoutedPropertyChangedEventArgs args) { Axis axis = (Axis)sender; if (args.NewValue == AxisLocation.Auto) { throw new InvalidOperationException(Properties.Resources.Chart_AxisLocationChanged_CantBeChangedToAutoWhenHostedInsideOfASeriesHost); } SetEdge(axis); _edgeAxes.Remove(axis); _edgeAxes.Add(axis); } /// /// Adds a series to the plot area and injects chart services. /// /// The series to add to the plot area. private void AddSeriesToPlotArea(ISeries series) { series.SeriesHost = this; AggregatedObservableCollection chartLegendItems = this.LegendItems as AggregatedObservableCollection; int indexOfSeries = this.Series.IndexOf(series); chartLegendItems.ChildCollections.Insert(indexOfSeries, series.LegendItems); } /// /// Builds the visual tree for the Chart control when a new template /// is applied. /// public override void OnApplyTemplate() { // Call base implementation base.OnApplyTemplate(); // Unhook events from former template parts if (null != ChartArea) { ChartArea.Children.Clear(); } if (null != Legend) { Legend.ItemsSource = null; } // Access new template parts ChartArea = GetTemplateChild(ChartAreaName) as EdgePanel; Legend = GetTemplateChild(LegendName) as Legend; SelectionArea = GetTemplateChild(SelectionAreaName) as Canvas; PlotArea = GetTemplateChild(PlotAreaName) as Grid; CrosshairContainer = GetTemplateChild(CrosshairContainerName) as Grid; Crosshair = GetTemplateChild("PART_Crosshair") as Grid; LocationIndicator = GetTemplateChild("PART_LocationIndicator") as Border; if (ChartArea != null) { _chartAreaChildrenListAdapter.TargetList = ChartArea.Children; _chartAreaChildrenListAdapter.Populate(); } if (Legend != null) { Legend.ItemsSource = this.LegendItems; } if (SelectionArea != null) { SelectionArea.MouseLeftButtonDown += SelectionArea_MouseLeftButtonDown; } if (CrosshairContainer != null) { CrosshairContainer.MouseEnter += CrosshairContainer_MouseEnter; CrosshairContainer.MouseLeave += CrosshairContainer_MouseLeave; CrosshairContainer.MouseMove += CrosshairContainer_MouseMove; } } private KeyValuePair GetPlotAreaCoordinates(Point position) { if (this.ActualAxes.Count >= 2) // && Axes[0] is IRangeAxis && Axes[1] is IRangeAxis) { object yAxisHit = null; object xAxisHit = null; if (this.ActualAxes[0].Orientation == AxisOrientation.Y) { if (ActualAxes[0] is IRangeAxis) { yAxisHit = ((IRangeAxis)this.ActualAxes[0]).GetValueAtPosition(new UnitValue(PlotArea.ActualHeight - position.Y, Unit.Pixels)); } else if (ActualAxes[0] is ICategoryAxis) { yAxisHit = ((ICategoryAxis)this.ActualAxes[0]).GetCategoryAtPosition(new UnitValue(/*PlotArea.ActualHeight -*/ position.Y, Unit.Pixels)); } } if (this.ActualAxes[0].Orientation == AxisOrientation.X) { if (ActualAxes[0] is IRangeAxis) { xAxisHit = ((IRangeAxis)this.ActualAxes[0]).GetValueAtPosition(new UnitValue(position.X, Unit.Pixels)); } else if (ActualAxes[0] is ICategoryAxis) { xAxisHit = ((ICategoryAxis)this.ActualAxes[0]).GetCategoryAtPosition(new UnitValue(position.X, Unit.Pixels)); } } if (this.ActualAxes[1].Orientation == AxisOrientation.Y) { if (ActualAxes[1] is IRangeAxis) { yAxisHit = ((IRangeAxis)this.ActualAxes[1]).GetValueAtPosition(new UnitValue(PlotArea.ActualHeight - position.Y, Unit.Pixels)); } else if (ActualAxes[1] is ICategoryAxis) { yAxisHit = ((ICategoryAxis)this.ActualAxes[1]).GetCategoryAtPosition(new UnitValue(/*PlotArea.ActualHeight -*/ position.Y, Unit.Pixels)); } } if (this.ActualAxes[1].Orientation == AxisOrientation.X) { if (ActualAxes[1] is IRangeAxis) { xAxisHit = ((IRangeAxis)this.ActualAxes[1]).GetValueAtPosition(new UnitValue(position.X, Unit.Pixels)); } else if (ActualAxes[1] is ICategoryAxis) { xAxisHit = ((ICategoryAxis)this.ActualAxes[1]).GetCategoryAtPosition(new UnitValue(position.X, Unit.Pixels)); } } return new KeyValuePair(xAxisHit, yAxisHit); } return new KeyValuePair(); } void CrosshairContainer_MouseMove(object sender, MouseEventArgs e) { Point mousePos = e.GetPosition(PlotArea); var crosshairLocation = GetPlotAreaCoordinates(mousePos); LocationIndicator.DataContext = crosshairLocation; Crosshair.DataContext = mousePos; } void CrosshairContainer_MouseLeave(object sender, MouseEventArgs e) { SetCrossHairVisibility(false); } private void CrosshairContainer_MouseEnter(object sender, MouseEventArgs e) { SetCrossHairVisibility(true); } private void SetCrossHairVisibility(bool visible) { LocationIndicator.Visibility = visible ? Visibility.Visible : Visibility.Collapsed; Crosshair.Visibility = visible ? Visibility.Visible : Visibility.Collapsed; this.Cursor = visible ? Cursors.None : Cursors.Arrow; } private Rectangle SelectionRect; private Point SelectionStartPoint; private void SelectionArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (Keyboard.Modifiers == ModifierKeys.Control) { //if (currentOrderList != null && currentOrderList.Count <= 7) // return; SelectionStartPoint = e.GetPosition((sender as Canvas)); if (SelectionRect == null) { SelectionRect = new Rectangle(); SelectionArea.Children.Add(SelectionRect); Canvas.SetLeft(SelectionRect, SelectionStartPoint.X); Canvas.SetTop(SelectionRect, 0); SelectionRect.Height = PlotArea.ActualHeight; SelectionRect.Opacity = .5; SelectionRect.Fill = new SolidColorBrush(Colors.LightGray); SelectionRect.Stroke = new SolidColorBrush(Colors.Gray); SelectionRect.StrokeThickness = 2.0; } } else { //LineSeries ls = this.chart1.Series[0] as LineSeries; //if (OrdersStack.Count <= 1) //{ // ls.ItemsSource = orders; // currentOrderList = null; // while (OrdersStack.Count > 0) // OrdersStack.Pop(); // return; //} //else //{ // OrdersStack.Pop(); // currentOrderList = OrdersStack.Pop(); // ls.ItemsSource = currentOrderList; //} } } /// /// Ensures that ISeriesHost is in a consistent state when axes collection is /// changed. /// /// Event source. /// Event arguments. private void ActualAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (Axis axis in e.NewItems.OfType()) { AddAxisToChartArea(axis); } } if (e.OldItems != null) { foreach (Axis axis in e.OldItems.OfType()) { RemoveAxisFromChartArea(axis); } } } /// /// Removes an axis from the Chart area. /// /// The axis to remove from the ISeriesHost area. private void RemoveAxisFromChartArea(Axis axis) { axis.LocationChanged -= AxisLocationChanged; axis.OrientationChanged -= AxisOrientationChanged; IRequireSeriesHost requiresSeriesHost = axis as IRequireSeriesHost; if (requiresSeriesHost != null) { requiresSeriesHost.SeriesHost = null; } _edgeAxes.Remove(axis); } /// /// Removes a series from the plot area. /// /// The series to remove from the plot area. /// private void RemoveSeriesFromPlotArea(ISeries series) { AggregatedObservableCollection legendItemsList = LegendItems as AggregatedObservableCollection; legendItemsList.ChildCollections.Remove(series.LegendItems); series.SeriesHost = null; } /// /// Called when the ObservableCollection.CollectionChanged property /// changes. /// /// The object that raised the event. /// The event data. private void SeriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Clear ISeriesHost property of old Series if (null != e.OldItems) { foreach (ISeries series in e.OldItems) { ISeriesHost host = series as ISeriesHost; if (host != null) { foreach (IRequireGlobalSeriesIndex tracksGlobalIndex in host.GetDescendentSeries().OfType()) { tracksGlobalIndex.GlobalSeriesIndexChanged(null); } host.Series.CollectionChanged -= new NotifyCollectionChangedEventHandler(ChildSeriesCollectionChanged); } IRequireGlobalSeriesIndex require = series as IRequireGlobalSeriesIndex; if (require != null) { require.GlobalSeriesIndexChanged(null); } RemoveSeriesFromPlotArea(series); } } // Set ISeriesHost property of new Series if (null != e.NewItems) { foreach (ISeries series in e.NewItems) { ISeriesHost host = series as ISeriesHost; if (null != host) { host.Series.CollectionChanged += new NotifyCollectionChangedEventHandler(ChildSeriesCollectionChanged); } AddSeriesToPlotArea(series); } } if (e.Action != NotifyCollectionChangedAction.Replace) { OnGlobalSeriesIndexesInvalidated(this, new RoutedEventArgs()); } } /// /// Handles changes to the collections of child ISeries implementing ISeriesHost. /// /// Event source. /// Event arguments. private void ChildSeriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { OnGlobalSeriesIndexesInvalidated(this, new RoutedEventArgs()); } /// /// Returns a rotating enumerator of ResourceDictionary objects that coordinates /// with the dispenser object to ensure that no two enumerators are on the same /// item. If the dispenser is reset or its collection is changed then the /// enumerators are also reset. /// /// A predicate that returns a value indicating /// whether to return an item. /// An enumerator of ResourceDictionaries. public IEnumerator GetResourceDictionariesWhere(Func predicate) { return ResourceDictionaryDispenser.GetResourceDictionariesWhere(predicate); } /// /// Updates the global indexes of all descendents that require a global /// index. /// /// The source of the event. /// The event data. private void OnGlobalSeriesIndexesInvalidated(object sender, RoutedEventArgs args) { UpdateGlobalIndexes(); } /// /// Updates the global index property of all Series that track their /// global index. /// private void UpdateGlobalIndexes() { (this as ISeriesHost).GetDescendentSeries().OfType().ForEachWithIndex( (seriesThatTracksGlobalIndex, index) => { seriesThatTracksGlobalIndex.GlobalSeriesIndexChanged(index); }); } /// /// Gets or sets the Series host of the chart. /// /// This will always return null. ISeriesHost IRequireSeriesHost.SeriesHost { get { return SeriesHost; } set { SeriesHost = value; } } /// /// Gets or sets the Series host of the chart. /// /// This will always return null. protected ISeriesHost SeriesHost { get; set; } /// /// Gets the axes collection of the chart. /// ObservableCollection ISeriesHost.Axes { get { return InternalActualAxes as ObservableCollection; } } /// /// Gets the Series collection of the chart. /// ObservableCollection ISeriesHost.Series { get { return (ObservableCollection)Series; } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Chart/SeriesHostAxesCollection.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using System.Windows; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An axes collection used by a series host. /// internal class SeriesHostAxesCollection : UniqueObservableCollection { /// /// Gets or sets the series host field. /// private ISeriesHost SeriesHost { get; set; } /// /// Gets or sets a collection of axes cannot be removed under any /// circumstances. /// private UniqueObservableCollection PersistentAxes { get; set; } /// /// Instantiates a new instance of the SeriesHostAxesCollection class. /// /// The series host. internal SeriesHostAxesCollection(ISeriesHost seriesHost) { this.SeriesHost = seriesHost; this.PersistentAxes = new UniqueObservableCollection(); this.CollectionChanged += ThisCollectionChanged; } /// /// Instantiates a new instance of the SeriesHostAxesCollection class. /// /// The series host. /// A collection of axes that can never be /// removed from the chart. internal SeriesHostAxesCollection(ISeriesHost seriesHost, UniqueObservableCollection persistentAxes) : this(seriesHost) { Debug.Assert(persistentAxes != null, "Persistent axes collection cannot be null."); this.SeriesHost = seriesHost; this.PersistentAxes = persistentAxes; this.PersistentAxes.CollectionChanged += PersistentAxesCollectionChanged; } /// /// A method that attaches and removes listeners to axes added to this /// collection. /// /// This object. /// Information about the event. private void ThisCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (IAxis axis in e.NewItems) { axis.RegisteredListeners.CollectionChanged += AxisRegisteredListenersCollectionChanged; } } if (e.OldItems != null) { foreach (IAxis axis in e.OldItems) { axis.RegisteredListeners.CollectionChanged -= AxisRegisteredListenersCollectionChanged; } } } /// /// Remove an axis from the collection if it is no longer used. /// /// The axis that has had its registered /// listeners collection changed. /// Information about the event. private void AxisRegisteredListenersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { IAxis axis = this.Where(currentAxis => currentAxis.RegisteredListeners == sender).First(); if (e.OldItems != null) { if (!PersistentAxes.Contains(axis) && !SeriesHost.IsUsedByASeries(axis)) { this.Remove(axis); } } } /// /// This method synchronizes the collection with the persistent axes /// collection when it is changed. /// /// The source of the event. /// Information about the event. public void PersistentAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (IAxis axis in e.NewItems) { if (!this.Contains(axis)) { this.Add(axis); } } } if (e.OldItems != null) { foreach (IAxis axis in e.OldItems) { if (this.Contains(axis) && !SeriesHost.IsUsedByASeries(axis)) { this.Remove(axis); } } } } /// /// Removes an item from the axes collection but throws an exception /// if a series in the series host is listening to it. /// /// The index of the item being removed. protected override void RemoveItem(int index) { IAxis axis = this[index]; if (SeriesHost.IsUsedByASeries(axis)) { throw new InvalidOperationException(Properties.Resources.SeriesHostAxesCollection_RemoveItem_AxisCannotBeRemovedFromASeriesHostWhenOneOrMoreSeriesAreListeningToIt); } else if (PersistentAxes.Contains(axis)) { throw new InvalidOperationException(Properties.Resources.SeriesHostAxesCollection_InvalidAttemptToRemovePermanentAxisFromSeriesHost); } else { base.RemoveItem(index); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/AreaDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for an area series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public partial class AreaDataPoint : DataPoint { /// /// Initializes the static members of the AreaDataPoint class. /// static AreaDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AreaDataPoint), new FrameworkPropertyMetadata(typeof(AreaDataPoint))); } /// /// Initializes a new instance of the AreaDataPoint class. /// public AreaDataPoint() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/BarDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for a bar series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public partial class BarDataPoint : DataPoint { /// /// Initializes the static members of the BarDataPoint class. /// static BarDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BarDataPoint), new FrameworkPropertyMetadata(typeof(BarDataPoint))); } /// /// Initializes a new instance of the BarDataPoint class. /// public BarDataPoint() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/BubbleDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for a bubble series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public class BubbleDataPoint : DataPoint { #region public double Size /// /// Gets or sets the size value of the bubble data point. /// public double Size { get { return (double)GetValue(SizeProperty); } set { SetValue(SizeProperty, value); } } /// /// Identifies the Size dependency property. /// public static readonly DependencyProperty SizeProperty = DependencyProperty.Register( "Size", typeof(double), typeof(BubbleDataPoint), new PropertyMetadata(0.0, OnSizePropertyChanged)); /// /// SizeProperty property changed handler. /// /// BubbleDataPoint that changed its Size. /// Event arguments. private static void OnSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { BubbleDataPoint source = (BubbleDataPoint)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnSizePropertyChanged(oldValue, newValue); } /// /// SizeProperty property changed handler. /// /// Old value. /// New value. private void OnSizePropertyChanged(double oldValue, double newValue) { RoutedPropertyChangedEventHandler handler = SizePropertyChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } if (this.State == DataPointState.Created) { this.ActualSize = newValue; } } /// /// This event is raised when the size property is changed. /// internal event RoutedPropertyChangedEventHandler SizePropertyChanged; #endregion public double Size #region public double ActualSize /// /// Gets or sets the actual size of the bubble data point. /// public double ActualSize { get { return (double)GetValue(ActualSizeProperty); } set { SetValue(ActualSizeProperty, value); } } /// /// Identifies the ActualSize dependency property. /// public static readonly DependencyProperty ActualSizeProperty = DependencyProperty.Register( "ActualSize", typeof(double), typeof(BubbleDataPoint), new PropertyMetadata(0.0, OnActualSizePropertyChanged)); /// /// ActualSizeProperty property changed handler. /// /// BubbleDataPoint that changed its ActualSize. /// Event arguments. private static void OnActualSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { BubbleDataPoint source = (BubbleDataPoint)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnActualSizePropertyChanged(oldValue, newValue); } /// /// ActualSizeProperty property changed handler. /// /// Old value. /// New value. private void OnActualSizePropertyChanged(double oldValue, double newValue) { RoutedPropertyChangedEventHandler handler = ActualSizePropertyChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } /// /// This event is raised when the actual size property is changed. /// internal event RoutedPropertyChangedEventHandler ActualSizePropertyChanged; #endregion public double ActualSize /// /// Initializes the static members of the BubbleDataPoint class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static BubbleDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BubbleDataPoint), new FrameworkPropertyMetadata(typeof(BubbleDataPoint))); } /// /// Initializes a new instance of the bubble data point. /// public BubbleDataPoint() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/CandlestickDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for scatter series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public sealed partial class CandlestickDataPoint : DataPoint { #region public double Open /// /// Gets or sets the size value of the bubble data point. /// public double Open { get { return (double)GetValue(OpenProperty); } set { SetValue(OpenProperty, value); } } /// /// Identifies the Size dependency property. /// public static readonly DependencyProperty OpenProperty = DependencyProperty.Register( "Open", typeof(double), typeof(CandlestickDataPoint), new PropertyMetadata(0.0)); #endregion public double Open #region public double Close /// /// Gets or sets the size value of the bubble data point. /// public double Close { get { return (double)GetValue(CloseProperty); } set { SetValue(CloseProperty, value); } } /// /// Identifies the Size dependency property. /// public static readonly DependencyProperty CloseProperty = DependencyProperty.Register( "Close", typeof(double), typeof(CandlestickDataPoint), new PropertyMetadata(0.0)); #endregion public double Close #region public double High /// /// Gets or sets the size value of the bubble data point. /// public double High { get { return (double)GetValue(HighProperty); } set { SetValue(HighProperty, value); } } /// /// Identifies the Size dependency property. /// public static readonly DependencyProperty HighProperty = DependencyProperty.Register( "High", typeof(double), typeof(CandlestickDataPoint), new PropertyMetadata(0.0)); #endregion public double High #region public double Low /// /// Gets or sets the size value of the bubble data point. /// public double Low { get { return (double)GetValue(LowProperty); } set { SetValue(LowProperty, value); } } /// /// Identifies the Size dependency property. /// public static readonly DependencyProperty LowProperty = DependencyProperty.Register( "Low", typeof(double), typeof(CandlestickDataPoint), new PropertyMetadata(0.0)); #endregion public double Low private const string BodyName = "PART_Body"; private Grid Body { get; set; } private const string ShadowName = "PART_Shadow"; private Grid Shadow { get; set; } /// /// Initializes the static members of the BarDataPoint class. /// static CandlestickDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CandlestickDataPoint), new FrameworkPropertyMetadata(typeof(CandlestickDataPoint))); } /// /// Initializes a new instance of the ScatterDataPoint class. /// public CandlestickDataPoint() { } public override void OnApplyTemplate() { base.OnApplyTemplate(); Body = GetTemplateChild(BodyName) as Grid; Shadow = GetTemplateChild(ShadowName) as Grid; } public void UpdateBody(IRangeAxis rangeAxis) { if (Body == null) return; double highPointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(High)).Value; double lowPointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(Low)).Value; double openPointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(Open)).Value; double closePointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(Close)).Value; Thickness margin; if (openPointY > closePointY) { margin = new Thickness(0, highPointY - openPointY, 0, closePointY - lowPointY); } else { margin = new Thickness(0, highPointY - closePointY, 0, openPointY - lowPointY); } Body.Margin = margin; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/ColumnDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for a column series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public partial class ColumnDataPoint : DataPoint { /// /// Initializes the static members of the ColumnDataPoint class. /// static ColumnDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ColumnDataPoint), new FrameworkPropertyMetadata(typeof(ColumnDataPoint))); } /// /// Initializes a new instance of the ColumnDataPoint class. /// public ColumnDataPoint() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/DataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that displays a data point. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public abstract partial class DataPoint : Control { #region CommonStates /// /// Common state group. /// internal const string GroupCommonStates = "CommonStates"; /// /// Normal state of the Common group. /// internal const string StateCommonNormal = "Normal"; /// /// MouseOver state of the Common group. /// internal const string StateCommonMouseOver = "MouseOver"; #endregion CommonStates #region SelectionStates /// /// Selection state group. /// internal const string GroupSelectionStates = "SelectionStates"; /// /// Unselected state of the Selection group. /// internal const string StateSelectionUnselected = "Unselected"; /// /// Selected state of the Selection group. /// internal const string StateSelectionSelected = "Selected"; #endregion SelectionStates #region GroupRevealStates /// /// Reveal state group. /// internal const string GroupRevealStates = "RevealStates"; /// /// Shown state of the Reveal group. /// internal const string StateRevealShown = "Shown"; /// /// Hidden state of the Reveal group. /// internal const string StateRevealHidden = "Hidden"; #endregion GroupRevealStates #region public bool IsSelectionEnabled /// /// Gets or sets a value indicating whether selection is enabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register( "IsSelectionEnabled", typeof(bool), typeof(DataPoint), new PropertyMetadata(false, OnIsSelectionEnabledPropertyChanged)); /// /// IsSelectionEnabledProperty property changed handler. /// /// Control that changed its IsSelectionEnabled. /// Event arguments. private static void OnIsSelectionEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsSelectionEnabledPropertyChanged(oldValue, newValue); } /// /// IsSelectionEnabledProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsSelectionEnabledPropertyChanged(bool oldValue, bool newValue) { if (newValue == false) { IsSelected = false; IsHovered = false; } } #endregion public bool IsSelectionEnabled /// /// Gets a value indicating whether the data point is active. /// internal bool IsActive { get; private set; } /// /// An event raised when the IsSelected property is changed. /// internal event RoutedPropertyChangedEventHandler IsSelectedChanged; /// /// A value indicating whether the mouse is hovering over the data /// point. /// private bool _isHovered; /// /// Gets a value indicating whether the mouse is hovering over /// the data point. /// protected bool IsHovered { get { return _isHovered; } private set { bool oldValue = _isHovered; _isHovered = value; if (oldValue != _isHovered) { OnIsHoveredPropertyChanged(oldValue, value); } } } /// /// IsHoveredProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsHoveredPropertyChanged(bool oldValue, bool newValue) { VisualStateManager.GoToState(this, (newValue == true) ? StateCommonMouseOver : StateCommonNormal, true); } #region internal bool IsSelected /// /// Gets or sets a value indicating whether the data point is selected. /// internal bool IsSelected { get { return (bool)GetValue(IsSelectedProperty); } set { SetValue(IsSelectedProperty, value); } } /// /// Identifies the IsSelected dependency property. /// internal static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register( "IsSelected", typeof(bool), typeof(DataPoint), new PropertyMetadata(false, OnIsSelectedPropertyChanged)); /// /// IsSelectedProperty property changed handler. /// /// Control that changed its IsSelected. /// Event arguments. private static void OnIsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsSelectedPropertyChanged(oldValue, newValue); } /// /// IsSelectedProperty property changed handler. /// /// The value to be replaced. /// The new value. protected virtual void OnIsSelectedPropertyChanged(bool oldValue, bool newValue) { VisualStateManager.GoToState(this, newValue ? StateSelectionSelected : StateSelectionUnselected, true); RoutedPropertyChangedEventHandler handler = this.IsSelectedChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } #endregion internal bool IsSelected /// /// Event raised when the actual dependent value of the data point is changed. /// internal event RoutedPropertyChangedEventHandler ActualDependentValueChanged; #region public IComparable ActualDependentValue /// /// Gets or sets the actual dependent value displayed in the chart. /// public IComparable ActualDependentValue { get { return (IComparable)GetValue(ActualDependentValueProperty); } set { SetValue(ActualDependentValueProperty, value); } } /// /// Identifies the ActualDependentValue dependency property. /// public static readonly System.Windows.DependencyProperty ActualDependentValueProperty = System.Windows.DependencyProperty.Register( "ActualDependentValue", typeof(IComparable), typeof(DataPoint), new System.Windows.PropertyMetadata(0.0, OnActualDependentValuePropertyChanged)); /// /// Called when the value of the ActualDependentValue property changes. /// /// Control that changed its ActualDependentValue. /// Event arguments. private static void OnActualDependentValuePropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; IComparable oldValue = (IComparable)e.OldValue; IComparable newValue = (IComparable)e.NewValue; source.OnActualDependentValuePropertyChanged(oldValue, newValue); } /// /// A value indicating whether the actual independent value is being /// coerced. /// private bool _isCoercingActualDependentValue; /// /// The preserved previous actual dependent value before coercion. /// private IComparable _oldActualDependentValueBeforeCoercion; /// /// Called when the value of the ActualDependentValue property changes. /// /// The value to be replaced. /// The new value. protected virtual void OnActualDependentValuePropertyChanged(IComparable oldValue, IComparable newValue) { double coercedValue = 0.0; if (!(newValue is double) && ValueHelper.TryConvert(newValue, out coercedValue)) { _isCoercingActualDependentValue = true; _oldActualDependentValueBeforeCoercion = oldValue; } if (!_isCoercingActualDependentValue) { if (_oldActualDependentValueBeforeCoercion != null) { oldValue = _oldActualDependentValueBeforeCoercion; _oldActualDependentValueBeforeCoercion = null; } RoutedPropertyChangedEventHandler handler = this.ActualDependentValueChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } if (_isCoercingActualDependentValue) { _isCoercingActualDependentValue = false; this.ActualDependentValue = coercedValue; } } #endregion public IComparable ActualDependentValue /// /// This event is raised when the dependent value of the data point is /// changed. /// internal event RoutedPropertyChangedEventHandler DependentValueChanged; #region public IComparable DependentValue /// /// Gets or sets the dependent value of the Control. /// public IComparable DependentValue { get { return (IComparable) GetValue(DependentValueProperty); } set { SetValue(DependentValueProperty, value); } } /// /// Identifies the DependentValue dependency property. /// public static readonly DependencyProperty DependentValueProperty = DependencyProperty.Register( "DependentValue", typeof(IComparable), typeof(DataPoint), new PropertyMetadata(null, OnDependentValuePropertyChanged)); /// /// Called when the DependentValue property changes. /// /// Control that changed its DependentValue. /// Event arguments. private static void OnDependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; IComparable oldValue = (IComparable) e.OldValue; IComparable newValue = (IComparable) e.NewValue; source.OnDependentValuePropertyChanged(oldValue, newValue); } /// /// Called when the DependentValue property changes. /// /// The value to be replaced. /// The new value. protected virtual void OnDependentValuePropertyChanged(IComparable oldValue, IComparable newValue) { SetFormattedProperty(FormattedDependentValueProperty, DependentValueStringFormat, newValue); RoutedPropertyChangedEventHandler handler = this.DependentValueChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } if (this.State == DataPointState.Created) { // Prefer setting the value as a double... double coercedNewValue; if (ValueHelper.TryConvert(newValue, out coercedNewValue)) { ActualDependentValue = coercedNewValue; } else { // ... but fall back otherwise ActualDependentValue = newValue; } } } #endregion public IComparable DependentValue #region public string DependentValueStringFormat /// /// Gets or sets the format string for the FormattedDependentValue property. /// public string DependentValueStringFormat { get { return GetValue(DependentValueStringFormatProperty) as string; } set { SetValue(DependentValueStringFormatProperty, value); } } /// /// Identifies the DependentValueStringFormat dependency property. /// public static readonly DependencyProperty DependentValueStringFormatProperty = DependencyProperty.Register( "DependentValueStringFormat", typeof(string), typeof(DataPoint), new PropertyMetadata(null, OnDependentValueStringFormatPropertyChanged)); /// /// Called when DependentValueStringFormat property changes. /// /// Control that changed its DependentValueStringFormat. /// Event arguments. private static void OnDependentValueStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = d as DataPoint; string oldValue = e.OldValue as string; string newValue = e.NewValue as string; source.OnDependentValueStringFormatPropertyChanged(oldValue, newValue); } /// /// Called when DependentValueStringFormat property changes. /// /// The value to be replaced. /// The new value. protected virtual void OnDependentValueStringFormatPropertyChanged(string oldValue, string newValue) { SetFormattedProperty(FormattedDependentValueProperty, newValue, DependentValue); } #endregion public string DependentValueStringFormat #region public string FormattedDependentValue /// /// Gets the DependentValue as formatted by the DependentValueStringFormat property. /// public string FormattedDependentValue { get { return GetValue(FormattedDependentValueProperty) as string; } } /// /// Identifies the FormattedDependentValue dependency property. /// public static readonly DependencyProperty FormattedDependentValueProperty = DependencyProperty.Register( "FormattedDependentValue", typeof(string), typeof(DataPoint), null); #endregion public string FormattedDependentValue #region public string FormattedIndependentValue /// /// Gets the IndependentValue as formatted by the IndependentValueStringFormat property. /// public string FormattedIndependentValue { get { return GetValue(FormattedIndependentValueProperty) as string; } } /// /// Identifies the FormattedIndependentValue dependency property. /// public static readonly DependencyProperty FormattedIndependentValueProperty = DependencyProperty.Register( "FormattedIndependentValue", typeof(string), typeof(DataPoint), null); #endregion public string FormattedIndependentValue /// /// Called when the independent value of the data point is changed. /// internal event RoutedPropertyChangedEventHandler IndependentValueChanged; #region public object IndependentValue /// /// Gets or sets the independent value. /// public object IndependentValue { get { return GetValue(IndependentValueProperty); } set { SetValue(IndependentValueProperty, value); } } /// /// Identifies the IndependentValue dependency property. /// public static readonly DependencyProperty IndependentValueProperty = DependencyProperty.Register( "IndependentValue", typeof(object), typeof(DataPoint), new PropertyMetadata(null, OnIndependentValuePropertyChanged)); /// /// Called when the IndependentValue property changes. /// /// Control that changed its IndependentValue. /// Event arguments. private static void OnIndependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; object oldValue = e.OldValue; object newValue = e.NewValue; source.OnIndependentValuePropertyChanged(oldValue, newValue); } /// /// Called when the IndependentValue property changes. /// /// The old value. /// The new value. protected virtual void OnIndependentValuePropertyChanged(object oldValue, object newValue) { SetFormattedProperty(FormattedIndependentValueProperty, IndependentValueStringFormat, newValue); RoutedPropertyChangedEventHandler handler = this.IndependentValueChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } if (this.State == DataPointState.Created) { // Prefer setting the value as a double... double coercedNewValue; if (ValueHelper.TryConvert(newValue, out coercedNewValue)) { ActualIndependentValue = coercedNewValue; } else { // ... but fall back otherwise ActualIndependentValue = newValue; } } } #endregion public object IndependentValue #region public string IndependentValueStringFormat /// /// Gets or sets the format string for the FormattedIndependentValue property. /// public string IndependentValueStringFormat { get { return GetValue(IndependentValueStringFormatProperty) as string; } set { SetValue(IndependentValueStringFormatProperty, value); } } /// /// Identifies the IndependentValueStringFormat dependency property. /// public static readonly DependencyProperty IndependentValueStringFormatProperty = DependencyProperty.Register( "IndependentValueStringFormat", typeof(string), typeof(DataPoint), new PropertyMetadata(null, OnIndependentValueStringFormatPropertyChanged)); /// /// Called when the value of the IndependentValueStringFormat property changes. /// /// Control that changed its IndependentValueStringFormat. /// Event arguments. private static void OnIndependentValueStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = d as DataPoint; string oldValue = e.OldValue as string; string newValue = e.NewValue as string; source.OnIndependentValueStringFormatPropertyChanged(oldValue, newValue); } /// /// Called when the value of the IndependentValueStringFormat property changes. /// /// The value to be replaced. /// The new value. protected virtual void OnIndependentValueStringFormatPropertyChanged(string oldValue, string newValue) { SetFormattedProperty(FormattedIndependentValueProperty, newValue, IndependentValue); } #endregion public string IndependentValueStringFormat /// /// Occurs when the actual independent value of the data point is /// changed. /// internal event RoutedPropertyChangedEventHandler ActualIndependentValueChanged; #region public object ActualIndependentValue /// /// Gets or sets the actual independent value. /// public object ActualIndependentValue { get { return (object)GetValue(ActualIndependentValueProperty); } set { SetValue(ActualIndependentValueProperty, value); } } /// /// A value indicating whether the actual independent value is being /// coerced. /// private bool _isCoercingActualIndependentValue; /// /// The preserved previous actual dependent value before coercion. /// private object _oldActualIndependentValueBeforeCoercion; /// /// Identifies the ActualIndependentValue dependency property. /// public static readonly DependencyProperty ActualIndependentValueProperty = DependencyProperty.Register( "ActualIndependentValue", typeof(object), typeof(DataPoint), new PropertyMetadata(OnActualIndependentValuePropertyChanged)); /// /// Called when the ActualIndependentValue property changes. /// /// Control that changed its ActualIndependentValue. /// Event arguments. private static void OnActualIndependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; object oldValue = (object)e.OldValue; object newValue = (object)e.NewValue; source.OnActualIndependentValuePropertyChanged(oldValue, newValue); } /// /// Called when the ActualIndependentValue property changes. /// /// The value to be replaced. /// The new value. protected virtual void OnActualIndependentValuePropertyChanged(object oldValue, object newValue) { double coercedValue = 0.0; if (!(newValue is double) && ValueHelper.TryConvert(newValue, out coercedValue)) { _isCoercingActualIndependentValue = true; _oldActualIndependentValueBeforeCoercion = oldValue; } if (!_isCoercingActualIndependentValue) { if (_oldActualIndependentValueBeforeCoercion != null) { oldValue = _oldActualIndependentValueBeforeCoercion; _oldActualIndependentValueBeforeCoercion = null; } RoutedPropertyChangedEventHandler handler = this.ActualIndependentValueChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } if (_isCoercingActualIndependentValue) { _isCoercingActualIndependentValue = false; this.ActualIndependentValue = coercedValue; } } #endregion public object ActualIndependentValue /// /// Occurs when the state of a data point is changed. /// internal event RoutedPropertyChangedEventHandler StateChanged; #region public DataPointState State /// /// Gets or sets a value indicating whether the State property is being /// coerced to its previous value. /// private bool IsCoercingState { get; set; } /// /// Gets or sets the state of the data point. /// internal DataPointState State { get { return (DataPointState)GetValue(StateProperty); } set { SetValue(StateProperty, value); } } /// /// Identifies the State dependency property. /// internal static readonly DependencyProperty StateProperty = DependencyProperty.Register( "State", typeof(DataPointState), typeof(DataPoint), new PropertyMetadata(DataPointState.Created, OnStatePropertyChanged)); /// /// Called when the value of the State property changes. /// /// Control that changed its State. /// Event arguments. private static void OnStatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPoint source = (DataPoint)d; DataPointState oldValue = (DataPointState)e.OldValue; DataPointState newValue = (DataPointState)e.NewValue; source.OnStatePropertyChanged(oldValue, newValue); } /// /// Called when the value of the State property changes. /// /// The value to be replaced. /// The new value. protected virtual void OnStatePropertyChanged(DataPointState oldValue, DataPointState newValue) { if (!IsCoercingState) { // If state ever goes to or past PendingRemoval, the DataPoint is no longer active if (DataPointState.PendingRemoval <= newValue) { IsActive = false; } if (newValue < oldValue) { // If we've somehow gone backwards in the life cycle (other // than when we go back to normal from updating) coerce to // old value. IsCoercingState = true; this.State = oldValue; IsCoercingState = false; } else { // Update selection if (newValue > DataPointState.Normal) { this.IsSelectionEnabled = false; } // Start state transition bool transitionStarted = false; switch (newValue) { case DataPointState.Showing: case DataPointState.Hiding: transitionStarted = GoToCurrentRevealState(); break; } // Fire Changed event RoutedPropertyChangedEventHandler handler = this.StateChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } // Change state if no transition started if (!transitionStarted && _templateApplied) { switch (newValue) { case DataPointState.Showing: State = DataPointState.Normal; break; case DataPointState.Hiding: State = DataPointState.Hidden; break; } } } } } #endregion internal DataPointState State /// /// Gets the implementation root of the Control. /// private FrameworkElement ImplementationRoot { get { return (1 == VisualTreeHelper.GetChildrenCount(this)) ? VisualTreeHelper.GetChild(this, 0) as FrameworkElement : null; } } /// /// Tracks whether the Reveal/Shown VisualState is available. /// private bool _haveStateRevealShown; /// /// Tracks whether the Reveal/Hidden VisualState is available. /// private bool _haveStateRevealHidden; /// /// Tracks whether the template has been applied yet. /// private bool _templateApplied; /// /// Initializes a new instance of the DataPoint class. /// protected DataPoint() { Loaded += new RoutedEventHandler(OnLoaded); IsActive = true; } /// /// Updates the Control's visuals to reflect the current state(s). /// /// True if a state transition was started. private bool GoToCurrentRevealState() { bool transitionStarted = false; string stateName = null; switch (State) { case DataPointState.Showing: if (_haveStateRevealShown) { stateName = StateRevealShown; } break; case DataPointState.Hiding: if (_haveStateRevealHidden) { stateName = StateRevealHidden; } break; } if (null != stateName) { if (!DesignerProperties.GetIsInDesignMode(this)) { // The use of Dispatcher.BeginInvoke here is necessary to // work around the StackOverflowException Silverlight throws // when it tries to play too many VSM animations. Dispatcher.BeginInvoke(new Action(() => VisualStateManager.GoToState(this, stateName, true))); } else { VisualStateManager.GoToState(this, stateName, false); } transitionStarted = true; } return transitionStarted; } /// /// Builds the visual tree for the DataPoint when a new template is applied. /// public override void OnApplyTemplate() { // Unhook CurrentStateChanged handler VisualStateGroup groupReveal = VisualStateManager.GetVisualStateGroups(ImplementationRoot).CastWrapper().Where(group => GroupRevealStates == group.Name).FirstOrDefault(); if (null != groupReveal) { groupReveal.CurrentStateChanged -= new EventHandler(OnCurrentStateChanged); } base.OnApplyTemplate(); // Hook CurrentStateChanged handler _haveStateRevealShown = false; _haveStateRevealHidden = false; groupReveal = VisualStateManager.GetVisualStateGroups(ImplementationRoot).CastWrapper().Where(group => GroupRevealStates == group.Name).FirstOrDefault(); if (null != groupReveal) { groupReveal.CurrentStateChanged += new EventHandler(OnCurrentStateChanged); _haveStateRevealShown = groupReveal.States.CastWrapper().Where(state => StateRevealShown == state.Name).Any(); _haveStateRevealHidden = groupReveal.States.CastWrapper().Where(state => StateRevealHidden == state.Name).Any(); } _templateApplied = true; // Go to current state(s) GoToCurrentRevealState(); if (DesignerProperties.GetIsInDesignMode(this)) { // Transition to Showing state in design mode so DataPoint will be visible State = DataPointState.Showing; } } /// /// Changes the DataPoint object's state after one of the VSM state animations completes. /// /// Event source. /// Event arguments. private void OnCurrentStateChanged(object sender, VisualStateChangedEventArgs e) { switch (e.NewState.Name) { case StateRevealShown: if (State == DataPointState.Showing) { State = DataPointState.Normal; } break; case StateRevealHidden: if (State == DataPointState.Hiding) { State = DataPointState.Hidden; } break; } } /// /// Handles the Control's Loaded event. /// /// The Control. /// Event arguments. private void OnLoaded(object sender, RoutedEventArgs e) { GoToCurrentRevealState(); } /// /// Provides handling for the MouseEnter event. /// /// Event arguments. protected override void OnMouseEnter(MouseEventArgs e) { base.OnMouseEnter(e); if (IsSelectionEnabled) { IsHovered = true; } } /// /// Provides handling for the MouseLeave event. /// /// Event arguments. protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); if (IsSelectionEnabled) { IsHovered = false; } } /// /// Provides handling for the MouseLeftButtonDown event. /// /// Event arguments. protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (DefinitionSeriesIsSelectionEnabledHandling) { // DefinitionSeries-compatible handling if (!IsSelectionEnabled) { // Prevents clicks from bubbling to background, but necessary // to avoid letting ListBoxItem select the item e.Handled = true; } base.OnMouseLeftButtonDown(e); } else { // Traditional handling base.OnMouseLeftButtonDown(e); if (IsSelectionEnabled) { IsSelected = (ModifierKeys.None == (ModifierKeys.Control & Keyboard.Modifiers)); e.Handled = true; } } } /// /// Gets or sets a value indicating whether to handle IsSelectionEnabled in the DefinitionSeries manner. /// internal bool DefinitionSeriesIsSelectionEnabledHandling { get; set; } /// /// Sets a dependency property with the specified format. /// /// The DependencyProperty to set. /// The Format string to apply to the value. /// The value of the dependency property to be formatted. internal void SetFormattedProperty(DependencyProperty property, string format, object value) { SetValue(property, string.Format(CultureInfo.CurrentCulture, format ?? "{0}", value)); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/DataPointState.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Describes the state a data point is in. /// public enum DataPointState { /// /// Data point has been created. /// Created, /// /// Data point is in the process of being revealed. /// Showing, /// /// Data point is visible in the plot area. /// Normal, /// /// Data point is in the process of being removed from the plot area. /// PendingRemoval, /// /// Data point is in the process of being hidden. /// Hiding, /// /// Data point is hidden. /// Hidden, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/LineDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for a line series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public partial class LineDataPoint : DataPoint { /// /// Initializes the static members of the LineDataPoint class. /// static LineDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(LineDataPoint), new FrameworkPropertyMetadata(typeof(LineDataPoint))); } /// /// Initializes a new instance of the LineDataPoint class. /// public LineDataPoint() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/PieDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for a pie series. /// /// Preview [TemplatePart(Name = SliceName, Type = typeof(UIElement))] [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public class PieDataPoint : DataPoint { /// /// The name of the slice template part. /// private const string SliceName = "Slice"; /// /// Name of the ActualDataPointStyle property. /// internal const string ActualDataPointStyleName = "ActualDataPointStyle"; #region public Geometry Geometry /// /// Gets or sets the Geometry property which defines the shape of the /// data point. /// public Geometry Geometry { get { return GetValue(GeometryProperty) as Geometry; } set { SetValue(GeometryProperty, value); } } /// /// Identifies the Geometry dependency property. /// public static readonly DependencyProperty GeometryProperty = DependencyProperty.Register( "Geometry", typeof(Geometry), typeof(PieDataPoint), null); #endregion public Geometry Geometry // GeometrySelection and GeometryHighlight exist on Silverlight because // a single Geometry object can not be the target of multiple // TemplateBindings - yet the default template has 3 Paths that bind. #region public Geometry GeometrySelection /// /// Gets or sets the Geometry which defines the shape of a point. The /// GeometrySelection property is a copy of the Geometry property. /// public Geometry GeometrySelection { get { return GetValue(GeometrySelectionProperty) as Geometry; } set { SetValue(GeometrySelectionProperty, value); } } /// /// Identifies the GeometrySelection dependency property. /// public static readonly DependencyProperty GeometrySelectionProperty = DependencyProperty.Register( "GeometrySelection", typeof(Geometry), typeof(PieDataPoint), null); #endregion public Geometry GeometrySelection #region public Geometry GeometryHighlight /// /// Gets or sets the GeometryHighlight property which is a clone of the /// Geometry property. /// public Geometry GeometryHighlight { get { return GetValue(GeometryHighlightProperty) as Geometry; } set { SetValue(GeometryHighlightProperty, value); } } /// /// Identifies the GeometryHighlight dependency property. /// public static readonly DependencyProperty GeometryHighlightProperty = DependencyProperty.Register( "GeometryHighlight", typeof(Geometry), typeof(PieDataPoint), null); #endregion public Geometry GeometryHighlight /// /// Occurs when the actual offset ratio of the pie data point changes. /// internal event RoutedPropertyChangedEventHandler ActualOffsetRatioChanged; #region public double ActualOffsetRatio /// /// Gets or sets the offset ratio that is displayed on the screen. /// public double ActualOffsetRatio { get { return (double)GetValue(ActualOffsetRatioProperty); } set { SetValue(ActualOffsetRatioProperty, value); } } /// /// Identifies the ActualOffsetRatio dependency property. /// public static readonly DependencyProperty ActualOffsetRatioProperty = DependencyProperty.Register( "ActualOffsetRatio", typeof(double), typeof(PieDataPoint), new PropertyMetadata(OnActualOffsetRatioPropertyChanged)); /// /// Called when the value of the ActualOffsetRatioProperty property changes. /// /// PieDataPoint that changed its ActualOffsetRatio. /// Event arguments. private static void OnActualOffsetRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PieDataPoint source = (PieDataPoint)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnActualOffsetRatioPropertyChanged(oldValue, newValue); } /// /// Called when the value of the ActualOffsetRatioProperty property changes. /// /// The value to be replaced. /// The new value. private void OnActualOffsetRatioPropertyChanged(double oldValue, double newValue) { RoutedPropertyChangedEventHandler handler = this.ActualOffsetRatioChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } if (DesignerProperties.GetIsInDesignMode(this)) { PieSeries.UpdatePieDataPointGeometry(this, ActualWidth, ActualHeight); } } #endregion public double ActualOffsetRatio /// /// An event raised when the actual ratio of the pie data point is /// changed. /// internal event RoutedPropertyChangedEventHandler ActualRatioChanged; #region public double ActualRatio /// /// Gets or sets the ratio displayed on the screen. /// public double ActualRatio { get { return (double)GetValue(ActualRatioProperty); } set { SetValue(ActualRatioProperty, value); } } /// /// Identifies the ActualRatio dependency property. /// public static readonly DependencyProperty ActualRatioProperty = DependencyProperty.Register( "ActualRatio", typeof(double), typeof(PieDataPoint), new PropertyMetadata(OnActualRatioPropertyChanged)); /// /// Called when the value of the ActualRatioProperty property changes. /// /// PieDataPoint that changed its ActualRatio. /// Event arguments. private static void OnActualRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PieDataPoint source = (PieDataPoint)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnActualRatioPropertyChanged(oldValue, newValue); } /// /// Called when the value of the ActualRatioProperty property changes. /// /// The value to be replaced. /// The new value. private void OnActualRatioPropertyChanged(double oldValue, double newValue) { if (ValueHelper.CanGraph(newValue)) { RoutedPropertyChangedEventHandler handler = this.ActualRatioChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } } else { this.ActualRatio = 0.0; } if (DesignerProperties.GetIsInDesignMode(this)) { PieSeries.UpdatePieDataPointGeometry(this, ActualWidth, ActualHeight); } } #endregion public double ActualRatio #region public string FormattedRatio /// /// Gets the Ratio with the value of the RatioStringFormat property applied. /// public string FormattedRatio { get { return GetValue(FormattedRatioProperty) as string; } } /// /// Identifies the FormattedRatio dependency property. /// public static readonly DependencyProperty FormattedRatioProperty = DependencyProperty.Register( "FormattedRatio", typeof(string), typeof(PieDataPoint), null); #endregion public string FormattedRatio /// /// An event raised when the offset ratio of the pie data point is /// changed. /// internal event RoutedPropertyChangedEventHandler OffsetRatioChanged; #region public double OffsetRatio /// /// Gets or sets the offset ratio of the pie data point. /// public double OffsetRatio { get { return (double)GetValue(OffsetRatioProperty); } set { SetValue(OffsetRatioProperty, value); } } /// /// Identifies the OffsetRatio dependency property. /// public static readonly DependencyProperty OffsetRatioProperty = DependencyProperty.Register( "OffsetRatio", typeof(double), typeof(PieDataPoint), new PropertyMetadata(OnOffsetRatioPropertyChanged)); /// /// Called when the value of the OffsetRatioProperty property changes. /// /// PieDataPoint that changed its OffsetRatio. /// Event arguments. private static void OnOffsetRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PieDataPoint source = (PieDataPoint)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnOffsetRatioPropertyChanged(oldValue, newValue); } /// /// Called when the value of the OffsetRatioProperty property changes. /// /// The value to be replaced. /// The new value. private void OnOffsetRatioPropertyChanged(double oldValue, double newValue) { if (ValueHelper.CanGraph(newValue)) { RoutedPropertyChangedEventHandler handler = this.OffsetRatioChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } if (this.State == DataPointState.Created) { ActualOffsetRatio = newValue; } } else { this.OffsetRatio = 0.0; } } #endregion public double OffsetRatio /// /// An event raised when the ratio of the pie data point is /// changed. /// internal event RoutedPropertyChangedEventHandler RatioChanged; #region public double Ratio /// /// Gets or sets the ratio of the total that the data point /// represents. /// public double Ratio { get { return (double)GetValue(RatioProperty); } set { SetValue(RatioProperty, value); } } /// /// Identifies the Ratio dependency property. /// public static readonly DependencyProperty RatioProperty = DependencyProperty.Register( "Ratio", typeof(double), typeof(PieDataPoint), new PropertyMetadata(OnRatioPropertyChanged)); /// /// Called when the value of the RatioProperty property changes. /// /// PieDataPoint that changed its Ratio. /// Event arguments. private static void OnRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PieDataPoint source = (PieDataPoint)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnRatioPropertyChanged(oldValue, newValue); } /// /// Called when the value of the RatioProperty property changes. /// /// The value to be replaced. /// The new value. private void OnRatioPropertyChanged(double oldValue, double newValue) { if (ValueHelper.CanGraph(newValue)) { SetFormattedProperty(FormattedRatioProperty, RatioStringFormat, newValue); RoutedPropertyChangedEventHandler handler = this.RatioChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } if (this.State == DataPointState.Created) { ActualRatio = newValue; } } else { this.Ratio = 0.0; } } #endregion public double Ratio #region public string RatioStringFormat /// /// Gets or sets the format string for the FormattedRatio property. /// public string RatioStringFormat { get { return GetValue(RatioStringFormatProperty) as string; } set { SetValue(RatioStringFormatProperty, value); } } /// /// Identifies the RatioStringFormat dependency property. /// public static readonly DependencyProperty RatioStringFormatProperty = DependencyProperty.Register( "RatioStringFormat", typeof(string), typeof(PieDataPoint), new PropertyMetadata(null, OnRatioStringFormatPropertyChanged)); /// /// Called when the value of the RatioStringFormatProperty property changes. /// /// PieDataPoint that changed its RatioStringFormat. /// Event arguments. private static void OnRatioStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PieDataPoint source = d as PieDataPoint; string newValue = e.NewValue as string; source.OnRatioStringFormatPropertyChanged(newValue); } /// /// Called when the value of the RatioStringFormatProperty property changes. /// /// The new value. private void OnRatioStringFormatPropertyChanged(string newValue) { SetFormattedProperty(FormattedRatioProperty, newValue, Ratio); } #endregion public string RatioStringFormat #region internal Style ActualDataPointStyle /// /// Gets or sets the actual style used for the data points. /// internal Style ActualDataPointStyle { get { return GetValue(ActualDataPointStyleProperty) as Style; } set { SetValue(ActualDataPointStyleProperty, value); } } /// /// Identifies the ActualDataPointStyle dependency property. /// internal static readonly DependencyProperty ActualDataPointStyleProperty = DependencyProperty.Register( ActualDataPointStyleName, typeof(Style), typeof(PieDataPoint), null); #endregion internal Style ActualDataPointStyle #region internal Style ActualLegendItemStyle /// /// Gets or sets the actual style used for the legend item. /// internal Style ActualLegendItemStyle { get { return GetValue(ActualLegendItemStyleProperty) as Style; } set { SetValue(ActualLegendItemStyleProperty, value); } } /// /// Identifies the ActualLegendItemStyle dependency property. /// internal static readonly DependencyProperty ActualLegendItemStyleProperty = DependencyProperty.Register( DataPointSeries.ActualLegendItemStyleName, typeof(Style), typeof(PieDataPoint), null); #endregion protected Style ActualLegendItemStyle /// /// Gets the Palette-dispensed ResourceDictionary for the Series. /// protected internal ResourceDictionary PaletteResources { get; internal set; } /// /// Gets or sets the element that represents the pie slice. /// private UIElement SliceElement { get; set; } /// /// Initializes the static members of the PieDataPoint class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static PieDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(PieDataPoint), new FrameworkPropertyMetadata(typeof(PieDataPoint))); } /// /// Initializes a new instance of the PieDataPoint class. /// public PieDataPoint() { if (DesignerProperties.GetIsInDesignMode(this)) { // Create default design-mode-friendly settings ActualRatio = 0.2; SizeChanged += delegate(object sender, SizeChangedEventArgs e) { // Handle SizeChanged event to update Geometry dynamically PieSeries.UpdatePieDataPointGeometry(this, e.NewSize.Width, e.NewSize.Height); }; } } /// /// Builds the visual tree for the PieDataPoint when a new template is applied. /// public override void OnApplyTemplate() { if (null != SliceElement) { SliceElement.MouseEnter -= new MouseEventHandler(SliceElement_MouseEnter); SliceElement.MouseLeave -= new MouseEventHandler(SliceElement_MouseLeave); } base.OnApplyTemplate(); SliceElement = GetTemplateChild(SliceName) as UIElement; if (null != SliceElement) { SliceElement.MouseEnter += new MouseEventHandler(SliceElement_MouseEnter); SliceElement.MouseLeave += new MouseEventHandler(SliceElement_MouseLeave); } } /// /// Provides handling for the MouseEnter event. /// /// The event data. protected override void OnMouseEnter(MouseEventArgs e) { // Do nothing because PieDataPoint handles SliceElement.MouseEnter instead } /// /// Provides handling for the MouseLeave event. /// /// The event data. protected override void OnMouseLeave(MouseEventArgs e) { // Do nothing because PieDataPoint handles SliceElement.MouseLeave instead } /// /// Provides handling for the MouseEnter event. /// /// Event source. /// The event data. private void SliceElement_MouseEnter(object sender, MouseEventArgs e) { // Defer to Control's default MouseEnter handling base.OnMouseEnter(e); } /// /// Provides handling for the MouseLeave event. /// /// Event source. /// The event data. private void SliceElement_MouseLeave(object sender, MouseEventArgs e) { // Defer to Control's default MouseLeave handling base.OnMouseLeave(e); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/DataPoint/ScatterDataPoint.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a data point used for a scatter series. /// /// Preview [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)] [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)] [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)] [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)] public partial class ScatterDataPoint : DataPoint { /// /// Initializes the static members of the ScatterDataPoint class. /// static ScatterDataPoint() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ScatterDataPoint), new FrameworkPropertyMetadata(typeof(ScatterDataPoint))); } /// /// Initializes a new instance of the ScatterDataPoint class. /// public ScatterDataPoint() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/FrameworkElementExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// A set of extension methods for the DataPoint class. /// internal static class FrameworkElementExtensions { /// /// Returns the actual margin for a given framework element and axis. /// /// The framework element. /// The axis along which to return the margin. /// /// The margin for a given framework element and axis. /// public static double GetActualMargin(this FrameworkElement element, IAxis axis) { double length = 0.0; if (axis.Orientation == AxisOrientation.X) { length = element.ActualWidth; } else if (axis.Orientation == AxisOrientation.Y) { length = element.ActualHeight; } return length / 2.0; } /// /// Returns the margin for a given framework element and axis. /// /// The framework element. /// The axis along which to return the margin. /// /// The margin for a given framework element and axis. /// public static double GetMargin(this FrameworkElement element, IAxis axis) { double length = 0.0; if (axis.Orientation == AxisOrientation.X) { length = !double.IsNaN(element.Width) ? element.Width : element.ActualWidth; } else if (axis.Orientation == AxisOrientation.Y) { length = !double.IsNaN(element.Height) ? element.Height : element.ActualHeight; } return length / 2.0; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Helper/Converters.cs ================================================ using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { public class DoubleToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { double doubleValue = (double)value; if (doubleValue == 0.0) { return Visibility.Collapsed; } return Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Helper/FormattingConverter.cs ================================================ using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { public class FormattingConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string formatString = parameter as string; return string.Format("{0:" + formatString + "}", value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Helper/TreeHelper.cs ================================================ using System.Windows.Media; namespace System.Windows.Controls.DataVisualization.Charting { internal static class TreeHelper { /// /// Finds the first ancestor of the element passed as a parameter that has type T. /// /// The type of the ancestor we're looking for. /// The element where we start our search. /// The first ancestor of element of type T. public static T FindAncestor(DependencyObject element) where T : class { while (element != null) { DependencyObject parent = VisualTreeHelper.GetParent(element) as DependencyObject; T result = parent as T; if (result != null) { return result; } element = parent; } return null; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/IRequireSeriesHost.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// An object that implements this interface requires a series host. /// public interface IRequireSeriesHost { /// /// Gets or sets the series host. /// ISeriesHost SeriesHost { get; set; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/ISeriesHost.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Defines properties, methods and events for classes that host a /// collection of Series objects. /// /// Preview public interface ISeriesHost : IRequireSeriesHost, IResourceDictionaryDispenser { /// /// Gets the collection of axes the series host has available. /// ObservableCollection Axes { get; } /// /// Gets the collection of series the series host has available. /// ObservableCollection Series { get; } /// /// Gets the foreground elements. /// ObservableCollection ForegroundElements { get; } /// /// Gets the background elements. /// ObservableCollection BackgroundElements { get; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/ISeriesHostExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Linq; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Extension methods for series hosts. /// internal static class ISeriesHostExtensions { /// /// Gets all series that track their global indexes recursively. /// /// The root series host. /// A sequence of series. public static IEnumerable GetDescendentSeries(this ISeriesHost rootSeriesHost) { Queue series = new Queue(rootSeriesHost.Series); while (series.Count != 0) { ISeries currentSeries = series.Dequeue(); yield return currentSeries; ISeriesHost seriesHost = currentSeries as ISeriesHost; if (seriesHost != null) { foreach (ISeries childSeries in seriesHost.Series) { series.Enqueue(childSeries); } } } } /// /// Gets a value indicating whether an axis is in use by the series /// host. /// /// The series host. /// The axis that may or may not be used by a /// series. /// A value indicating whether an axis is in use by the series /// host. public static bool IsUsedByASeries(this ISeriesHost that, IAxis axis) { return axis.RegisteredListeners.OfType().Intersect(that.Series).Any(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Pie/PieChartHelper.cs ================================================ using System.Diagnostics; using System.Windows.Media; namespace System.Windows.Controls.DataVisualization.Charting { internal static class PieChartHelper { const double DistanceTolerance = 0.01; const double DistanceSmallArc = 40; /// /// Gets the center and arc midpoint from a pie chart data point's geometry. /// It also determines if the arc of this geometry is small. /// /// The geometry of a pie chart data point. /// The center of the pie chart. /// The midpoint of the arc in the pie wedge. /// True if the arc of the geometry is small, false otherwise. /// True if the geometry is of the right form, false otherwise. public static bool GetPieChartInfo(Geometry geometry, out Point center, out Point arcMidpoint, out bool isArcSmall) { center = arcMidpoint = new Point(); isArcSmall = false; if (geometry == null) { return false; } EllipseGeometry ellipseGeometry = geometry as EllipseGeometry; if (ellipseGeometry != null) { center = ellipseGeometry.Center; arcMidpoint = center + new Vector(ellipseGeometry.RadiusX / Math.Sqrt(2), -ellipseGeometry.RadiusY / Math.Sqrt(2)); return true; } PathGeometry pathGeometry = geometry as PathGeometry; if (pathGeometry != null && pathGeometry.Figures.Count > 0) { PathFigure pathFigure = pathGeometry.Figures[0]; if (pathFigure != null && pathFigure.Segments.Count > 1) { center = pathFigure.StartPoint; LineSegment lineSegment = pathFigure.Segments[0] as LineSegment; ArcSegment arcSegment = pathFigure.Segments[1] as ArcSegment; if (lineSegment != null && arcSegment != null) { CalculateArcInfo(center, lineSegment.Point, arcSegment, out arcMidpoint, out isArcSmall); return true; } } } return false; } /// /// Calculates the midpoint of the arc passed as a parameter, as well as whether the arc is small. /// /// The center of the pie chart. /// The start point of the arc segment. /// The arc of the pie wedge itself. /// he midpoint of the arc in the pie wedge. /// True if the arc is small, false otherwise. private static void CalculateArcInfo(Point center, Point startPoint, ArcSegment arcSegment, out Point arcMidpoint, out bool isArcSmall) { // Note: we assume a valid arcSegment with equal radii. Debug.Assert(arcSegment != null); Debug.Assert(arcSegment.Size.Width == arcSegment.Size.Height); Point endPoint = arcSegment.Point; Point chordMidpoint = new Point(0.5 * (startPoint.X + endPoint.X), 0.5 * (startPoint.Y + endPoint.Y)); Vector chordDirection = endPoint - startPoint; double chordLength = chordDirection.Length; double radius = arcSegment.Size.Width; isArcSmall = chordLength < DistanceSmallArc; // If the chord length is less than the distance tolerance, just use the chord midpoint // or the point on the opposite side of the circle as appropriate. if (chordLength < DistanceTolerance) { arcMidpoint = arcSegment.IsLargeArc ? center - (chordMidpoint - center) : chordMidpoint; } else { chordDirection /= chordLength; Vector radialDirection = new Vector(-chordDirection.Y, chordDirection.X); double halfChordLength = 0.5 * chordLength; double radialOffset; if (radius >= halfChordLength) { double sectorRadius = Math.Sqrt(radius * radius - halfChordLength * halfChordLength); radialOffset = -radius + (arcSegment.IsLargeArc ? -sectorRadius : sectorRadius); } else { radialOffset = -halfChordLength; } if (arcSegment.SweepDirection == SweepDirection.Counterclockwise) { radialOffset = -radialOffset; } arcMidpoint = chordMidpoint + radialOffset * radialDirection; } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Pie/PieChartLabel.cs ================================================ using System.Linq; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// This is the control that holds the label. It includes an optional line connecting it to the chart, and whatever /// content the developer specifies in the DataTemplate. /// [TemplatePart(Name = "Canvas_PART", Type = typeof(Canvas))] [TemplatePart(Name = "Content_PART", Type = typeof(ContentPresenter))] public class PieChartLabel : ContentControl { private ContentPresenter contentPart; private Canvas canvasPart; private Point center; private Point arcMidpoint; public bool IsArcSmall { get; set; } static PieChartLabel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(PieChartLabel), new FrameworkPropertyMetadata(typeof(PieChartLabel))); } /// /// FormattedRation DP - When a PieChartLabel is created, this property is data bound to the PieDataPoint's equivalent /// property. It will typically be used within the DataTemplate for PieChartLabel. /// public static readonly DependencyProperty FormattedRatioProperty = PieDataPoint.FormattedRatioProperty.AddOwner(typeof(PieChartLabel), null); public string FormattedRatio { get { return (string)this.GetValue(FormattedRatioProperty); } set { this.SetValue(FormattedRatioProperty, value); } } /// /// Geometry DP - When a PieChartLabel is created, this property is data bound to the PieDataPoint's equivalent /// property. We need this information to calculate the position of the label. /// public static readonly DependencyProperty GeometryProperty = PieDataPoint.GeometryProperty.AddOwner(typeof(PieChartLabel), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.AffectsArrange, GeometryPropertyChanged)); public Geometry Geometry { get { return (Geometry)this.GetValue(GeometryProperty); } set { this.SetValue(GeometryProperty, value); } } private static void GeometryPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { PieChartLabel label = obj as PieChartLabel; if (label != null) { bool isArcSmall; PieChartHelper.GetPieChartInfo(e.NewValue as Geometry, out label.center, out label.arcMidpoint, out isArcSmall); label.IsArcSmall = isArcSmall; } } /// /// DisplayMode DP - The developer can set this DP to control how the labels should be displayed. /// This property can have one of the following values: /// ArcMidpoint - Center of the label is positioned at the arc midpoint. /// Connected - A line connecting the arc midpoint and label is displayed. /// Auto - If at least one pie slice is very small, all pie slices use the Connected display mode. Otherwise, they all use ArcMidpoint. /// AutoMixed - Connected display mode is used for all small pie slices, and ArcMidpoint is used for all other slices. /// InsideArc - Display the label inside ARC /// public DisplayMode DisplayMode { get { return (DisplayMode)this.GetValue(DisplayModeProperty); } set { this.SetValue(DisplayModeProperty, value); } } public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register("DisplayMode", typeof(DisplayMode), typeof(PieChartLabel), new FrameworkPropertyMetadata(DisplayMode.ArcMidpoint, FrameworkPropertyMetadataOptions.AffectsArrange)); /// /// LineStroke DP - Determines the brush of the line when in connected mode. /// public Brush LineStroke { get { return (Brush)this.GetValue(LineStrokeProperty); } set { this.SetValue(LineStrokeProperty, value); } } public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register("LineStroke", typeof(Brush), typeof(PieChartLabel), new PropertyMetadata(new SolidColorBrush(Colors.Gray))); /// /// LineStrokeThickness DP - Determines the thickness of the line when in connected mode. /// public double LineStrokeThickness { get { return (double)this.GetValue(LineStrokeThicknessProperty); } set { this.SetValue(LineStrokeThicknessProperty, value); } } public static readonly DependencyProperty LineStrokeThicknessProperty = DependencyProperty.Register("LineStrokeThickness", typeof(double), typeof(PieChartLabel), new PropertyMetadata(1.0)); /// /// On arrange, re-position the label. /// protected override Size ArrangeOverride(Size arrangeBounds) { this.PositionLabel(); return base.ArrangeOverride(arrangeBounds); } /// /// When the template is applied, get the template parts. Also, ensure that if the /// content of the label changes, the label is re-positioned. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); contentPart = this.GetTemplateChild("Content_PART") as ContentPresenter; canvasPart = this.GetTemplateChild("Canvas_PART") as Canvas; if (contentPart != null) { contentPart.SizeChanged += delegate (object sender, SizeChangedEventArgs e) { this.PositionLabel(); }; } } /// /// Positions the label depending on the display mode specified by the developer. /// private void PositionLabel() { switch (this.DisplayMode) { case DisplayMode.ArcMidpoint: this.PositionArcMidpoint(); break; case DisplayMode.Connected: this.PositionConnected(); break; case DisplayMode.AutoMixed: this.PositionAutoMixed(); break; case DisplayMode.Auto: this.PositionAuto(); break; case DisplayMode.InsideArc: this.PositionInsideArc(); break; } } const double RadionToDegree = 180.0 / Math.PI; private void PositionInsideArc() { this.RemovePolyline(); if (this.contentPart != null) { double left = this.center.X + (this.arcMidpoint.X - this.center.X) * 0.5 - this.contentPart.DesiredSize.Width * 0.5; // find center point with in Arc double top = this.center.Y + (this.arcMidpoint.Y - this.center.Y) * 0.5 - this.contentPart.DesiredSize.Height * 0.5; double labelAngle = Math.Atan2(this.arcMidpoint.Y - this.center.Y, (this.arcMidpoint.X - this.center.X)) * RadionToDegree; if (this.center.X > this.arcMidpoint.X) labelAngle = labelAngle - 180; // for Left to Right this.contentPart.RenderTransform = new RotateTransform() { Angle = labelAngle, }; this.contentPart.RenderTransformOrigin = new Point(.5, .5); Canvas.SetLeft(this.contentPart, left); Canvas.SetTop(this.contentPart, top); } } /// /// Positions the label with its center in the same location as the midpoint of the pie slice arc. /// private void PositionArcMidpoint() { this.RemovePolyline(); if (this.contentPart != null) { Canvas.SetTop(this.contentPart, this.arcMidpoint.Y - 0.5 * this.contentPart.DesiredSize.Height); Canvas.SetLeft(this.contentPart, this.arcMidpoint.X - 0.5 * this.contentPart.DesiredSize.Width); } } /// /// Adds a line that connects the arc midpoint to the label and positions the label appropriately. /// Ideally, I would add the Polyline in the template and I would only change its points here. Unfortunately, /// because of a WPF bug, the Polyline doesn't render in certain corner-case scenarios. As a workaround, /// I create a new Polyline everytime the label is positioned, as can be seen in this method. /// private void PositionConnected() { try { this.RemovePolyline(); if (this.contentPart != null) { PointCollection newPoints = new PointCollection(); // First point newPoints.Add(this.SnapPoint(this.arcMidpoint)); // Second point Vector radialDirection = this.arcMidpoint - this.center; radialDirection.Normalize(); Point secondPoint = this.arcMidpoint + (radialDirection * 10); newPoints.Add(this.SnapPoint(secondPoint)); // Third point int sign = Math.Sign(radialDirection.X); // 1 if label is on the right side, -1 if it's on the left. Point thirdPoint = secondPoint + new Vector(sign * 20, 0); newPoints.Add(this.SnapPoint(thirdPoint)); double contentX = (sign == 1) ? thirdPoint.X : thirdPoint.X - this.contentPart.DesiredSize.Width; double contentY = thirdPoint.Y - 0.5 * this.contentPart.DesiredSize.Height; Canvas.SetTop(this.contentPart, contentY); Canvas.SetLeft(this.contentPart, contentX); Polyline polyline = new Polyline(); polyline.Points = newPoints; polyline.SetBinding(Polyline.StrokeThicknessProperty, new Binding("LineStrokeThickness") { Source = this }); polyline.SetBinding(Polyline.StrokeProperty, new Binding("LineStroke") { Source = this }); polyline.StrokeLineJoin = PenLineJoin.Round; this.canvasPart.Children.Add(polyline); } } catch (Exception) { } } /// /// Removes the Polyline from the canvas part. /// private void RemovePolyline() { if (this.canvasPart != null) { Polyline polyline = canvasPart.Children.OfType().FirstOrDefault(); if (polyline != null) { canvasPart.Children.Remove(polyline); } } } /// /// Positions the label in the arc midpoint if it's big enough, and displays it connected by a line if it's /// small. /// private void PositionAutoMixed() { if (this.IsArcSmall) { this.PositionConnected(); } else { this.PositionArcMidpoint(); } } /// /// If at least one arc is small, all labels are displayed connected by a line. Otherwise, they're positioned /// in the arc midpoint. /// private void PositionAuto() { Chart chart = TreeHelper.FindAncestor(this); if (chart != null) { PieChartLabelArea labelArea = chart.Template.FindName("LabelArea_PART", chart) as PieChartLabelArea; if (labelArea != null && labelArea.HasSmallArc) { this.PositionConnected(); } else { this.PositionArcMidpoint(); } } } /// /// Ensures that the line connecting the label is positioned in such a way that whole pixels are used to render /// it. This calculation depends on whether the thickness of the line is odd or even. /// /// A point belonging to the polyline. /// The point that will cause the line to snap to pixels. private Point SnapPoint(Point point) { double lineThickness = this.LineStrokeThickness; int intLineThickness = (int)lineThickness; if (lineThickness == intLineThickness) { if ((intLineThickness % 2) == 1) { return new Point(Math.Floor(point.X) + 0.5, Math.Floor(point.Y) + 0.5); } else { return new Point(Math.Round(point.X), Math.Round(point.Y)); } } return point; } /// /// When a label is clicked, the corresponding PieDataPoint becomes selected. /// This enables master-detail scenario driven by clicking on labels. /// /// protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { Chart chart = TreeHelper.FindAncestor(this); if (chart != null) { PieSeries pieSeries = chart.Series.OfType().FirstOrDefault(); if (pieSeries != null) { pieSeries.SelectedItem = this.Content; } } } } /// /// Modes used to control how the labels are positioned. /// public enum DisplayMode { ArcMidpoint, // Center of the label is positioned at the arc midpoint. Connected, // A line connecting the arc midpoint and label is displayed. Auto, // If at least one pie slice is very small, all pie slices use the Connected display mode. Otherwise, they all use ArcMidpoint. AutoMixed, // Connected display mode is used for all small pie slices, and ArcMidpoint is used for all other slices. InsideArc // Label is placed inside Arc, centered, text is left to right } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Pie/PieChartLabelArea.cs ================================================ using System.Linq; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Canvas where all labels (of type PieChartLabel) will be added. We need a separate /// canvas to avoid z-order issues. This prevents having a pie slice overlap a label. /// public class PieChartLabelArea : Canvas { /// /// HasSmallArc DP - If at least one slice of the pie chart is small, this property is set /// to true. This is used in the "Auto" mode. When in this mode, if this property is set to /// true, all labels will be connected to the chart by a line. /// public bool HasSmallArc { get { return (bool)this.GetValue(HasSmallArcProperty); } set { this.SetValue(HasSmallArcProperty, value); } } public static readonly DependencyProperty HasSmallArcProperty = DependencyProperty.Register("HasSmallArc", typeof(bool), typeof(PieChartLabelArea), new PropertyMetadata(false, HasSmallArcPropertyChanged)); /// /// When the HasSmallArc DO is set, all labels should be re-arranged. /// private static void HasSmallArcPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { PieChartLabelArea labelArea = obj as PieChartLabelArea; if (labelArea != null) { foreach (PieChartLabel label in labelArea.Children.OfType()) { label.InvalidateArrange(); } } } /// /// On measure, we check if there is at least one child of the canvas that is small, and we set /// HasSmallArc to reflect that information. /// Setting HasSmallArc ends up causing all labels to be re-arranged taking this information /// into consideration. /// protected override Size MeasureOverride(Size constraint) { bool hasSmallArc = false; foreach (PieChartLabel label in this.Children.OfType()) { if (label.IsArcSmall) { hasSmallArc = true; break; } } this.HasSmallArc = hasSmallArc; return base.MeasureOverride(constraint); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Primitives/DelegatingListBox.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting.Primitives { /// /// Subclasses ListBox to provide an easy way for a consumer of /// ListBox to hook into the four standard ListBox *Container* /// overrides. /// /// Preview public class DelegatingListBox : ListBox { /// /// Gets or sets a function to call when the /// IsItemItsOwnContainerOverride method executes. /// public Func IsItemItsOwnContainer { get; set; } /// /// Gets or sets a function to call when the /// GetContainerForItem method executes. /// public Func GetContainerForItem { get; set; } /// /// Gets or sets an action to call when the /// PrepareContainerForItem method executes. /// public Action PrepareContainerForItem { get; set; } /// /// Gets or sets an action to call when the /// ClearContainerForItem method executes. /// public Action ClearContainerForItem { get; set; } /// /// Initializes static members of the DelegatingListBox class. /// static DelegatingListBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DelegatingListBox), new FrameworkPropertyMetadata(typeof(DelegatingListBox))); } /// /// Initializes a new instance of the DelegatingListBox class. /// public DelegatingListBox() { } /// /// Determines if the specified item is (or is eligible to be) its own container. /// /// The item to check. /// True if the item is (or is eligible to be) its own container; otherwise, false. protected override bool IsItemItsOwnContainerOverride(object item) { return (null != IsItemItsOwnContainer) ? IsItemItsOwnContainer(item) : base.IsItemItsOwnContainerOverride(item); } /// /// Creates or identifies the element that is used to display the given item. /// /// The element that is used to display the given item. protected override DependencyObject GetContainerForItemOverride() { return (null != GetContainerForItem) ? GetContainerForItem() : base.GetContainerForItemOverride(); } /// /// Prepares the specified element to display the specified item. /// /// The element used to display the specified item. /// The item to display. protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { base.PrepareContainerForItemOverride(element, item); if (null != PrepareContainerForItem) { PrepareContainerForItem(element, item); } } /// /// Undoes the effects of the PrepareContainerForItemOverride method. /// /// The container element. /// The item to display. protected override void ClearContainerForItemOverride(DependencyObject element, object item) { base.ClearContainerForItemOverride(element, item); if (null != ClearContainerForItem) { ClearContainerForItem(element, item); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Primitives/Edge.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; namespace System.Windows.Controls.DataVisualization.Charting.Primitives { /// /// Specifies the edge position of a child element that is inside an /// EdgePanel. /// /// Preview public enum Edge { /// /// A child element that is positioned in the center of a EdgePanel. /// Center, /// /// A child element that is positioned on the left side of the /// EdgePanel. /// Left, /// /// A child element that is positioned at the top of the EdgePanel. /// Top, /// /// A child element that is positioned on the right side of the /// EdgePanel. /// Right, /// /// A child element that is positioned at the bottom of the EdgePanel. /// Bottom, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Primitives/EdgePanel.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Windows.Media; namespace System.Windows.Controls.DataVisualization.Charting.Primitives { /// /// Defines an area where you can arrange child elements either horizontally /// or vertically, relative to each other. /// /// Preview public class EdgePanel : Panel { /// /// The maximum number of iterations. /// private const int MaximumIterations = 10; /// /// A flag that ignores a property change when set. /// private static bool _ignorePropertyChange; #region public attached Edge Edge /// /// Gets the value of the Edge attached property for a specified /// UIElement. /// /// /// The element from which the property value is read. /// /// The Edge property value for the element. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "EdgePanel only has UIElement children")] public static Edge GetEdge(UIElement element) { if (element == null) { throw new ArgumentNullException("element"); } return (Edge)element.GetValue(EdgeProperty); } /// /// Sets the value of the Edge attached property to a specified element. /// /// /// The element to which the attached property is written. /// /// The needed Edge value. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "EdgePanel only has UIElement children")] public static void SetEdge(UIElement element, Edge edge) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(EdgeProperty, edge); } /// /// Identifies the Edge dependency property. /// public static readonly DependencyProperty EdgeProperty = DependencyProperty.RegisterAttached( "Edge", typeof(Edge), typeof(EdgePanel), new PropertyMetadata(Edge.Center, OnEdgePropertyChanged)); /// /// EdgeProperty property changed handler. /// /// UIElement that changed its Edge. /// Event arguments. [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Almost always set from the attached property CLR setter.")] private static void OnEdgePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Ignore the change if requested if (_ignorePropertyChange) { _ignorePropertyChange = false; return; } UIElement element = (UIElement)d; Edge value = (Edge)e.NewValue; // Validate the Edge property if ((value != Edge.Left) && (value != Edge.Top) && (value != Edge.Right) && (value != Edge.Center) && (value != Edge.Bottom)) { // Reset the property to its original state before throwing _ignorePropertyChange = true; element.SetValue(EdgeProperty, (Edge)e.OldValue); string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.EdgePanel_OnEdgePropertyChanged, value); throw new ArgumentException(message, "value"); } // Cause the EdgePanel to update its layout when a child changes EdgePanel panel = VisualTreeHelper.GetParent(element) as EdgePanel; if (panel != null) { panel.InvalidateMeasure(); } } #endregion public attached Edge Edge /// /// Initializes a new instance of the EdgePanel class. /// public EdgePanel() { this.SizeChanged += new SizeChangedEventHandler(EdgePanelSizeChanged); } /// /// Invalidate measure when edge panel is resized. /// /// The source of the event. /// Information about the event. private void EdgePanelSizeChanged(object sender, SizeChangedEventArgs e) { InvalidateMeasure(); } /// /// The left rectangle in which to render left elements. /// private Rect _leftRect; /// /// The right rectangle in which to render right elements. /// private Rect _rightRect; /// /// The top rectangle in which to render top elements. /// private Rect _topRect; /// /// The bottom rectangle in which to render bottom elements. /// private Rect _bottomRect; /// /// Measures the children of a EdgePanel in anticipation of arranging /// them during the ArrangeOverride pass. /// /// A maximum Size to not exceed. /// The desired size of the EdgePanel. [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification = "Code is by nature difficult to refactor into several methods.")] [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")] [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Splitting up method will make it more difficult to understand.")] protected override Size MeasureOverride(Size constraint) { constraint = new Size(this.ActualWidth, this.ActualHeight); IList leftElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Left).ToList(); IList rightElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Right).ToList(); IList bottomElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Bottom).ToList(); IList topElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Top).ToList(); Rect totalRect = SafeCreateRect(0, 0, constraint.Width, constraint.Height); _leftRect = (leftElements.Count > 0) ? totalRect : Rect.Empty; _bottomRect = (bottomElements.Count > 0) ? totalRect : Rect.Empty; _rightRect = (rightElements.Count > 0) ? totalRect : Rect.Empty; _topRect = (topElements.Count > 0) ? totalRect : Rect.Empty; double rightAxesWidth = 0.0; double leftAxesWidth = 0.0; double topAxesHeight = 0.0; double bottomAxesHeight = 0.0; double maxRightRequestedWidth = 0; double maxLeftRequestedWidth = 0; double maxTopRequestedHeight = 0; double maxBottomRequestedHeight = 0; double previousRightAxesWidth = rightAxesWidth; double previousLeftAxesWidth = leftAxesWidth; double previousTopAxesHeight = topAxesHeight; double previousBottomAxesHeight = bottomAxesHeight; int iterations = 0; // Measure each of the Children while (true) { // Measure the children using the rectangle regions. if (rightElements.Count > 0) { Size rightSize = new Size(constraint.Width, _rightRect.Height); foreach (UIElement rightUIElement in rightElements) { rightUIElement.Measure(rightSize); } previousRightAxesWidth = rightAxesWidth; rightAxesWidth = rightElements.Select(axis => axis.DesiredSize.Width).SumOrDefault(); maxRightRequestedWidth = Math.Max(maxRightRequestedWidth, rightAxesWidth); _rightRect = SafeCreateRect( constraint.Width - rightAxesWidth, _rightRect.Top, rightAxesWidth, _rightRect.Height); } if (topElements.Count > 0) { Size topSize = new Size(_topRect.Width, constraint.Height); foreach (UIElement topUIElement in topElements) { topUIElement.Measure(topSize); } previousTopAxesHeight = topAxesHeight; topAxesHeight = topElements.Select(axis => axis.DesiredSize.Height).SumOrDefault(); maxTopRequestedHeight = Math.Max(maxTopRequestedHeight, topAxesHeight); _topRect = SafeCreateRect( _topRect.Left, _topRect.Top, _topRect.Width, topAxesHeight); } if (leftElements.Count > 0) { Size leftSize = new Size(constraint.Width, _leftRect.Height); foreach (UIElement leftUIElement in leftElements) { leftUIElement.Measure(leftSize); } previousLeftAxesWidth = leftAxesWidth; leftAxesWidth = leftElements.Select(axis => axis.DesiredSize.Width).SumOrDefault(); maxLeftRequestedWidth = Math.Max(maxLeftRequestedWidth, leftAxesWidth); _leftRect = SafeCreateRect( _leftRect.Left, _leftRect.Top, leftElements.Select(axis => axis.DesiredSize.Width).SumOrDefault(), _leftRect.Height); } if (bottomElements.Count > 0) { Size bottomSize = new Size(_bottomRect.Width, constraint.Height); foreach (UIElement bottomUIElement in bottomElements) { bottomUIElement.Measure(bottomSize); } previousBottomAxesHeight = bottomAxesHeight; bottomAxesHeight = bottomElements.Select(axis => axis.DesiredSize.Height).SumOrDefault(); maxBottomRequestedHeight = Math.Max(maxBottomRequestedHeight, bottomAxesHeight); _bottomRect = SafeCreateRect( _bottomRect.Left, constraint.Height - bottomAxesHeight, _bottomRect.Width, bottomAxesHeight); } // Ensuring that parallel axes don't collide Rect leftRightCollisionRect = _leftRect; leftRightCollisionRect.Intersect(_rightRect); Rect topBottomCollisionRect = _topRect; topBottomCollisionRect.Intersect(_bottomRect); if (!leftRightCollisionRect.IsEmptyOrHasNoSize() || !topBottomCollisionRect.IsEmptyOrHasNoSize()) { return new Size(); } // Resolving perpendicular axes collisions Rect leftTopCollisionRect = _leftRect; leftTopCollisionRect.Intersect(_topRect); Rect rightTopCollisionRect = _rightRect; rightTopCollisionRect.Intersect(_topRect); Rect leftBottomCollisionRect = _leftRect; leftBottomCollisionRect.Intersect(_bottomRect); Rect rightBottomCollisionRect = _rightRect; rightBottomCollisionRect.Intersect(_bottomRect); if (leftBottomCollisionRect.IsEmptyOrHasNoSize() && rightBottomCollisionRect.IsEmptyOrHasNoSize() && leftTopCollisionRect.IsEmptyOrHasNoSize() && rightTopCollisionRect.IsEmptyOrHasNoSize() && previousBottomAxesHeight == bottomAxesHeight && previousLeftAxesWidth == leftAxesWidth && previousRightAxesWidth == rightAxesWidth && previousTopAxesHeight == topAxesHeight) { break; } if (iterations == MaximumIterations) { _leftRect = SafeCreateRect(0, maxTopRequestedHeight, maxLeftRequestedWidth, (constraint.Height - maxTopRequestedHeight) - maxBottomRequestedHeight); _rightRect = SafeCreateRect(constraint.Width - maxRightRequestedWidth, maxTopRequestedHeight, maxRightRequestedWidth, (constraint.Height - maxTopRequestedHeight) - maxBottomRequestedHeight); _bottomRect = SafeCreateRect(maxLeftRequestedWidth, constraint.Height - maxBottomRequestedHeight, (constraint.Width - maxLeftRequestedWidth) - maxRightRequestedWidth, maxBottomRequestedHeight); _topRect = SafeCreateRect(maxLeftRequestedWidth, 0, (constraint.Width - maxLeftRequestedWidth) - maxRightRequestedWidth, maxTopRequestedHeight); foreach (UIElement leftElement in leftElements) { leftElement.Measure(new Size(_leftRect.Width, _leftRect.Height)); } foreach (UIElement rightElement in rightElements) { rightElement.Measure(new Size(_rightRect.Width, _rightRect.Height)); } foreach (UIElement bottomElement in bottomElements) { bottomElement.Measure(new Size(_bottomRect.Width, _bottomRect.Height)); } foreach (UIElement topElement in topElements) { topElement.Measure(new Size(_topRect.Width, _topRect.Height)); } break; } if (!leftBottomCollisionRect.IsEmptyOrHasNoSize()) { _leftRect = SafeCreateRect( _leftRect.Left, _leftRect.Top, _leftRect.Width, _leftRect.Height - leftBottomCollisionRect.Height); _bottomRect = SafeCreateRect( _bottomRect.Left + leftBottomCollisionRect.Width, _bottomRect.Top, _bottomRect.Width - leftBottomCollisionRect.Width, _bottomRect.Height); } if (!leftTopCollisionRect.IsEmptyOrHasNoSize()) { _leftRect = SafeCreateRect( _leftRect.Left, _leftRect.Top + leftTopCollisionRect.Height, _leftRect.Width, _leftRect.Height - leftTopCollisionRect.Height); _topRect = SafeCreateRect( _topRect.Left + leftTopCollisionRect.Width, _topRect.Top, _topRect.Width - leftTopCollisionRect.Width, _topRect.Height); } if (!rightBottomCollisionRect.IsEmptyOrHasNoSize()) { _rightRect = SafeCreateRect( _rightRect.Left, _rightRect.Top, _rightRect.Width, _rightRect.Height - rightBottomCollisionRect.Height); _bottomRect = SafeCreateRect( _bottomRect.Left, _bottomRect.Top, _bottomRect.Width - rightBottomCollisionRect.Width, _bottomRect.Height); } if (!rightTopCollisionRect.IsEmptyOrHasNoSize()) { _rightRect = SafeCreateRect( _rightRect.Left, _rightRect.Top + rightTopCollisionRect.Height, _rightRect.Width, _rightRect.Height - rightTopCollisionRect.Height); _topRect = SafeCreateRect( _topRect.Left, _topRect.Top, _topRect.Width - rightTopCollisionRect.Width, _topRect.Height); } // Bring axis measure rectangles together if there are gaps // between them. if (!_leftRect.IsEmpty) { _leftRect = new Rect( new Point(_leftRect.Left, _topRect.BottomOrDefault(0)), new Point(_leftRect.Right, _bottomRect.TopOrDefault(constraint.Height))); } if (!_rightRect.IsEmpty) { _rightRect = new Rect( new Point(_rightRect.Left, _topRect.BottomOrDefault(0)), new Point(_rightRect.Right, _bottomRect.TopOrDefault(constraint.Height))); } if (!_bottomRect.IsEmpty) { _bottomRect = new Rect( new Point(_leftRect.RightOrDefault(0), _bottomRect.Top), new Point(_rightRect.LeftOrDefault(constraint.Width), _bottomRect.Bottom)); } if (!_topRect.IsEmpty) { _topRect = new Rect( new Point(_leftRect.RightOrDefault(0), _topRect.Top), new Point(_rightRect.LeftOrDefault(constraint.Width), _topRect.Bottom)); } iterations++; } Size centerSize = new Size( (constraint.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0), (constraint.Height - _topRect.HeightOrDefault(0)) - _bottomRect.HeightOrDefault(0)); foreach (UIElement element in Children.OfType().Where(child => GetEdge(child) == Edge.Center)) { element.Measure(centerSize); } return new Size(); } /// /// Arranges the content (child elements) of a EdgePanel element. /// /// /// The Size the EdgePanel uses to arrange its child elements. /// /// The arranged size of the EdgePanel. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Splitting up method will make it more difficult to understand.")] [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")] protected override Size ArrangeOverride(Size arrangeSize) { if (arrangeSize.Width == 0 || arrangeSize.Height == 0 || !ValueHelper.CanGraph(arrangeSize.Width) || !ValueHelper.CanGraph(arrangeSize.Height)) { return arrangeSize; } IList leftElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Left).ToList(); IList rightElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Right).ToList(); IList bottomElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Bottom).ToList(); IList topElements = this.Children.OfType().Where(element => GetEdge(element) == Edge.Top).ToList(); if (!_bottomRect.IsEmpty) { double workingHeight = _bottomRect.Top; foreach (UIElement bottomUIElement in bottomElements) { bottomUIElement.Arrange(SafeCreateRect(_leftRect.RightOrDefault(0), workingHeight, (arrangeSize.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0), bottomUIElement.DesiredSize.Height)); workingHeight += bottomUIElement.DesiredSize.Height; } } if (!_topRect.IsEmpty) { double workingTop = _topRect.Bottom; foreach (UIElement topUIElement in topElements) { workingTop -= topUIElement.DesiredSize.Height; topUIElement.Arrange(SafeCreateRect(_leftRect.RightOrDefault(0), workingTop, (arrangeSize.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0), topUIElement.DesiredSize.Height)); } } if (!_rightRect.IsEmpty) { double workingRight = _rightRect.Left; foreach (UIElement rightUIElement in rightElements) { rightUIElement.Arrange(SafeCreateRect(workingRight, _topRect.BottomOrDefault(0), rightUIElement.DesiredSize.Width, (arrangeSize.Height - _bottomRect.HeightOrDefault(0)) - _topRect.HeightOrDefault(0))); workingRight += rightUIElement.DesiredSize.Width; } } if (!_leftRect.IsEmpty) { double workingLeft = _leftRect.Right; foreach (UIElement leftUIElement in leftElements) { workingLeft -= leftUIElement.DesiredSize.Width; Rect leftRect = SafeCreateRect(workingLeft, _topRect.BottomOrDefault(0), leftUIElement.DesiredSize.Width, (arrangeSize.Height - _bottomRect.HeightOrDefault(0)) - _topRect.HeightOrDefault(0)); leftUIElement.Arrange(leftRect); } } Rect centerRect = SafeCreateRect( _leftRect.RightOrDefault(0), _topRect.BottomOrDefault(0), ((arrangeSize.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0)), ((arrangeSize.Height - _topRect.HeightOrDefault(0)) - _bottomRect.HeightOrDefault(0))); foreach (UIElement element in Children.OfType().Where(child => GetEdge(child) == Edge.Center)) { element.Arrange(centerRect); } return arrangeSize; } /// /// Creates a Rect safely by forcing width/height to be valid. /// /// Rect left parameter. /// Rect top parameter. /// Rect width parameter. /// Rect height parameter. /// New Rect struct. private static Rect SafeCreateRect(double left, double top, double width, double height) { return new Rect(left, top, Math.Max(0.0, width), Math.Max(0.0, height)); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/ResourceDictionaryDispensedEventArgs.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization { /// /// Information describing the ResourceDictionary dispensed when a /// ResourceDictionaryDispensed event is raised. /// internal class ResourceDictionaryDispensedEventArgs : EventArgs { /// /// Initializes a new instance of the ResourceDictionaryDispensedEventArgs class. /// /// The index of the ResourceDictionary dispensed. /// The ResourceDictionary dispensed. public ResourceDictionaryDispensedEventArgs(int index, ResourceDictionary resourceDictionary) { this.ResourceDictionary = resourceDictionary; this.Index = index; } /// /// Gets the index of the ResourceDictionary dispensed. /// public int Index { get; private set; } /// /// Gets the ResourceDictionary dispensed. /// public ResourceDictionary ResourceDictionary { get; private set; } /// /// Returns a value indicating whether two objects are equal. /// /// The other object. /// /// A value indicating whether the two objects are equal. /// public override bool Equals(object obj) { return base.Equals(obj); } /// /// Returns a hash code. /// /// A hash code. public override int GetHashCode() { return base.GetHashCode(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/ResourceDictionaryDispenser.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.DataVisualization.Charting { /// /// A class that rotates through a list of ResourceDictionaries. /// internal class ResourceDictionaryDispenser : IResourceDictionaryDispenser { /// /// A linked list of ResourceDictionaries dispensed. /// private LinkedList _resourceDictionariesDispensed = new LinkedList(); /// /// A bag of weak references to connected style enumerators. /// private WeakReferenceBag _resourceDictionaryEnumerators = new WeakReferenceBag(); /// /// Value indicating whether to ignore that the enumerator has /// dispensed a ResourceDictionary. /// private bool _ignoreResourceDictionaryDispensedByEnumerator; /// /// The list of ResourceDictionaries of rotate. /// private IList _resourceDictionaries; /// /// Gets or sets the list of ResourceDictionaries to rotate. /// public IList ResourceDictionaries { get { return _resourceDictionaries; } set { if (value != _resourceDictionaries) { { INotifyCollectionChanged notifyCollectionChanged = _resourceDictionaries as INotifyCollectionChanged; if (notifyCollectionChanged != null) { notifyCollectionChanged.CollectionChanged -= ResourceDictionariesCollectionChanged; } } _resourceDictionaries = value; { INotifyCollectionChanged notifyCollectionChanged = _resourceDictionaries as INotifyCollectionChanged; if (notifyCollectionChanged != null) { notifyCollectionChanged.CollectionChanged += ResourceDictionariesCollectionChanged; } } Reset(); } } } /// /// This method is raised when the ResourceDictionaries collection is changed. /// /// The source of the event. /// Information about the event. private void ResourceDictionariesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (!(e.Action == NotifyCollectionChangedAction.Add && (this.ResourceDictionaries.Count - e.NewItems.Count) == e.NewStartingIndex)) { Reset(); } } /// /// The parent of the ResourceDictionaryDispenser. /// private IResourceDictionaryDispenser _parent; /// /// Event that is invoked when the ResourceDictionaryDispenser's contents have changed. /// public event EventHandler ResourceDictionariesChanged; /// /// Gets or sets the parent of the ResourceDictionaryDispenser. /// public IResourceDictionaryDispenser Parent { get { return _parent; } set { if (_parent != value) { if (null != _parent) { _parent.ResourceDictionariesChanged -= new EventHandler(ParentResourceDictionariesChanged); } _parent = value; if (null != _parent) { _parent.ResourceDictionariesChanged += new EventHandler(ParentResourceDictionariesChanged); } OnParentChanged(); } } } /// /// Initializes a new instance of the ResourceDictionaryDispenser class. /// public ResourceDictionaryDispenser() { } /// /// Resets the state of the ResourceDictionaryDispenser and its enumerators. /// private void Reset() { OnResetting(); // Invoke event EventHandler handler = ResourceDictionariesChanged; if (null != handler) { handler.Invoke(this, EventArgs.Empty); } } /// /// Unregisters an enumerator so that it can be garbage collected. /// /// The enumerator. internal void Unregister(ResourceDictionaryEnumerator enumerator) { _resourceDictionaryEnumerators.Remove(enumerator); } /// /// Returns a rotating enumerator of ResourceDictionary objects that coordinates /// with the dispenser object to ensure that no two enumerators are on the same /// item. If the dispenser is reset or its collection is changed then the /// enumerators are also reset. /// /// A predicate that returns a value indicating /// whether to return an item. /// An enumerator of ResourceDictionaries. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Returning a usable enumerator instance.")] public IEnumerator GetResourceDictionariesWhere(Func predicate) { ResourceDictionaryEnumerator enumerator = new ResourceDictionaryEnumerator(this, predicate); _ignoreResourceDictionaryDispensedByEnumerator = true; try { foreach (ResourceDictionaryDispensedEventArgs args in _resourceDictionariesDispensed) { enumerator.ResourceDictionaryDispenserResourceDictionaryDispensed(this, args); } } finally { _ignoreResourceDictionaryDispensedByEnumerator = false; } _resourceDictionaryEnumerators.Add(enumerator); return enumerator; } /// /// This method is raised when an enumerator dispenses a ResourceDictionary. /// /// The source of the event. /// Information about the event. internal void EnumeratorResourceDictionaryDispensed(object sender, ResourceDictionaryDispensedEventArgs e) { if (!_ignoreResourceDictionaryDispensedByEnumerator) { OnEnumeratorResourceDictionaryDispensed(this, e); } } /// /// Raises the ParentChanged event. /// private void OnParentChanged() { foreach (ResourceDictionaryEnumerator enumerator in _resourceDictionaryEnumerators) { enumerator.ResourceDictionaryDispenserParentChanged(); } } /// /// Raises the EnumeratorResourceDictionaryDispensed event. /// /// The source of the event. /// Information about the event. private void OnEnumeratorResourceDictionaryDispensed(object source, ResourceDictionaryDispensedEventArgs args) { // Remove this item from the list of dispensed styles. _resourceDictionariesDispensed.Remove(args); // Add this item to the end of the list of dispensed styles. _resourceDictionariesDispensed.AddLast(args); foreach (ResourceDictionaryEnumerator enumerator in _resourceDictionaryEnumerators) { enumerator.ResourceDictionaryDispenserResourceDictionaryDispensed(source, args); } } /// /// This method raises the EnumeratorsResetting event. /// private void OnResetting() { _resourceDictionariesDispensed.Clear(); foreach (ResourceDictionaryEnumerator enumerator in _resourceDictionaryEnumerators) { enumerator.ResourceDictionaryDispenserResetting(); } } /// /// Handles the Parent's ResourceDictionariesChanged event. /// /// Parent instance. /// Event args. private void ParentResourceDictionariesChanged(object sender, EventArgs e) { Reset(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/ResourceDictionaryEnumerator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization.Charting { /// /// An enumerator that dispenses ResourceDictionaries sequentially by coordinating with /// related enumerators. Enumerators are related through an association /// with a parent ResourceDictionaryDispenser class. /// internal class ResourceDictionaryEnumerator : IEnumerator { /// /// The index of current item in the ResourceDictionaryDispenser's list. /// private int? index; /// /// Gets or sets the current ResourceDictionary. /// private ResourceDictionary CurrentResourceDictionary { get; set; } /// /// The parent enumerator. /// private IEnumerator _parentEnumerator; /// /// Gets the parent enumerator. /// private IEnumerator ParentEnumerator { get { if (_parentEnumerator == null && ResourceDictionaryDispenser.Parent != null) { _parentEnumerator = ResourceDictionaryDispenser.Parent.GetResourceDictionariesWhere(Predicate); } return _parentEnumerator; } } /// /// Initializes a new instance of a ResourceDictionaryEnumerator. /// /// The dispenser that dispensed this /// ResourceDictionaryEnumerator. /// A predicate used to determine which /// ResourceDictionaries to return. public ResourceDictionaryEnumerator(ResourceDictionaryDispenser dispenser, Func predicate) { ResourceDictionaryDispenser = dispenser; Predicate = predicate; } /// /// Called when the parent has changed. /// internal void ResourceDictionaryDispenserParentChanged() { _parentEnumerator = null; } /// /// Returns the index of the next suitable style in the list. /// /// The index at which to start looking. /// The index of the next suitable ResourceDictionary. private int? GetIndexOfNextSuitableResourceDictionary(int startIndex) { if (ResourceDictionaryDispenser.ResourceDictionaries == null || ResourceDictionaryDispenser.ResourceDictionaries.Count == 0) { return new int?(); } if (startIndex >= ResourceDictionaryDispenser.ResourceDictionaries.Count) { startIndex = 0; } int counter = startIndex; do { if (Predicate(ResourceDictionaryDispenser.ResourceDictionaries[counter])) { return counter; } counter = (counter + 1) % ResourceDictionaryDispenser.ResourceDictionaries.Count; } while (startIndex != counter); return new int?(); } /// /// Resets the dispenser. /// internal void ResourceDictionaryDispenserResetting() { if (!ShouldRetrieveFromParentEnumerator) { index = new int?(); } } /// /// Gets or sets a predicate that returns a value indicating whether a /// ResourceDictionary should be returned by this enumerator. /// /// A value indicating whether a ResourceDictionary can be returned by this /// enumerator. private Func Predicate { get; set; } /// /// This method is invoked when one of the related enumerator's /// dispenses. The enumerator checks to see if the item /// dispensed would've been the next item it would have returned. If /// so it updates it's index to the position after the previously /// returned item. /// /// The ResourceDictionaryDispenser. /// Information about the event. internal void ResourceDictionaryDispenserResourceDictionaryDispensed(object sender, ResourceDictionaryDispensedEventArgs e) { if (!ShouldRetrieveFromParentEnumerator && Predicate(e.ResourceDictionary)) { int? nextStyleIndex = GetIndexOfNextSuitableResourceDictionary(index ?? 0); if ((nextStyleIndex ?? -1) == e.Index) { index = (e.Index + 1) % ResourceDictionaryDispenser.ResourceDictionaries.Count; } } } /// /// Raises the EnumeratorResourceDictionaryDispensed. /// /// Information about the ResourceDictionary dispensed. protected virtual void OnStyleDispensed(ResourceDictionaryDispensedEventArgs args) { ResourceDictionaryDispenser.EnumeratorResourceDictionaryDispensed(this, args); } /// /// Gets the dispenser that dispensed this enumerator. /// public ResourceDictionaryDispenser ResourceDictionaryDispenser { get; private set; } /// /// Gets the current ResourceDictionary. /// public ResourceDictionary Current { get { return CurrentResourceDictionary; } } /// /// Gets the current ResourceDictionary. /// object System.Collections.IEnumerator.Current { get { return CurrentResourceDictionary; } } /// /// Moves to the next ResourceDictionary. /// /// A value indicating whether there are any more suitable /// ResourceDictionary. public bool MoveNext() { if (ShouldRetrieveFromParentEnumerator && ParentEnumerator != null) { bool isMore = ParentEnumerator.MoveNext(); if (isMore) { this.CurrentResourceDictionary = ParentEnumerator.Current; } return isMore; } index = GetIndexOfNextSuitableResourceDictionary(index ?? 0); if (index == null) { CurrentResourceDictionary = null; Dispose(); return false; } CurrentResourceDictionary = ResourceDictionaryDispenser.ResourceDictionaries[index.Value]; OnStyleDispensed(new ResourceDictionaryDispensedEventArgs(index.Value, CurrentResourceDictionary)); return true; } /// /// Gets a value indicating whether a enumerator should return ResourceDictionaries /// from its parent enumerator. /// private bool ShouldRetrieveFromParentEnumerator { get { return this.ResourceDictionaryDispenser.ResourceDictionaries == null; } } /// /// Resets the enumerator. /// public void Reset() { throw new NotSupportedException(Properties.Resources.ResourceDictionaryEnumerator_CantResetEnumeratorResetDispenserInstead); } /// /// Stops listening to the dispenser. /// public void Dispose() { if (_parentEnumerator != null) { _parentEnumerator.Dispose(); } this.ResourceDictionaryDispenser.Unregister(this); GC.SuppressFinalize(this); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/AreaSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in X/Y /// line format. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(AreaDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = "PathStyle", StyleTargetType = typeof(Path))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] public partial class AreaSeries : LineAreaBaseSeries, IAnchoredToOrigin { #region public Geometry Geometry /// /// Gets the geometry property. /// public Geometry Geometry { get { return GetValue(GeometryProperty) as Geometry; } private set { SetValue(GeometryProperty, value); } } /// /// Identifies the Geometry dependency property. /// public static readonly DependencyProperty GeometryProperty = DependencyProperty.Register( "Geometry", typeof(Geometry), typeof(AreaSeries), null); #endregion public Geometry Geometry #region public Style PathStyle /// /// Gets or sets the style of the Path object that follows the data /// points. /// public Style PathStyle { get { return GetValue(PathStyleProperty) as Style; } set { SetValue(PathStyleProperty, value); } } /// /// Identifies the PathStyle dependency property. /// public static readonly DependencyProperty PathStyleProperty = DependencyProperty.Register( "PathStyle", typeof(Style), typeof(AreaSeries), null); #endregion public Style PathStyle /// /// Initializes the static members of the AreaSeries class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static AreaSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AreaSeries), new FrameworkPropertyMetadata(typeof(AreaSeries))); } /// /// Initializes a new instance of the AreaSeries class. /// public AreaSeries() { } /// /// Acquire a horizontal linear axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => { IRangeAxis rangeAxis = axis as IRangeAxis; return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.Y; }, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null || (axis as IRangeAxis).Origin == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// /// Updates the Series shape object from a collection of Points. /// /// Collection of Points. protected override void UpdateShapeFromPoints(IEnumerable points) { UnitValue originCoordinate = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin); UnitValue maximumCoordinate = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum); if (points.Any() && ValueHelper.CanGraph(originCoordinate.Value) && ValueHelper.CanGraph(maximumCoordinate.Value)) { double originY = Math.Floor(originCoordinate.Value); PathFigure figure = new PathFigure(); figure.IsClosed = true; figure.IsFilled = true; double maximum = maximumCoordinate.Value; Point startPoint; IEnumerator pointEnumerator = points.GetEnumerator(); pointEnumerator.MoveNext(); startPoint = new Point(pointEnumerator.Current.X, maximum - originY); figure.StartPoint = startPoint; Point lastPoint; do { lastPoint = pointEnumerator.Current; figure.Segments.Add(new LineSegment { Point = pointEnumerator.Current }); } while (pointEnumerator.MoveNext()); figure.Segments.Add(new LineSegment { Point = new Point(lastPoint.X, maximum - originY) }); if (figure.Segments.Count > 1) { PathGeometry geometry = new PathGeometry(); geometry.Figures.Add(figure); Geometry = geometry; return; } } else { Geometry = null; } } /// /// Remove value margins from the side of the data points to ensure /// that area chart is flush against the edge of the chart. /// /// The value margin consumer. /// A sequence of value margins. protected override IEnumerable GetValueMargins(IValueMarginConsumer consumer) { if (consumer == ActualIndependentAxis) { return Enumerable.Empty(); } return base.GetValueMargins(consumer); } /// /// Gets the axis to which the series is anchored. /// IRangeAxis IAnchoredToOrigin.AnchoredAxis { get { return AnchoredAxis; } } /// /// Gets the axis to which the series is anchored. /// protected IRangeAxis AnchoredAxis { get { return ActualDependentRangeAxis; } } } } #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/BarSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in bar format. /// /// Preview [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(BarDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] public partial class BarSeries : ColumnBarBaseSeries { /// /// Initializes a new instance of the BarSeries class. /// public BarSeries() { } /// /// Acquire a horizontal category axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.Y, () => new CategoryAxis { Orientation = AxisOrientation.Y }, (axis) => { IRangeAxis rangeAxis = axis as IRangeAxis; return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.X; }, () => { IRangeAxis rangeAxis = CreateRangeAxisFromData(firstDataPoint.DependentValue); rangeAxis.Orientation = AxisOrientation.X; if (rangeAxis == null || rangeAxis.Origin == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } DisplayAxis axis = rangeAxis as DisplayAxis; if (axis != null) { axis.ShowGridLines = true; } return rangeAxis; }); } /// /// Updates each point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { if (SeriesHost == null || PlotArea == null) { return; } object category = dataPoint.ActualIndependentValue ?? (this.ActiveDataPoints.IndexOf(dataPoint) + 1); Range coordinateRange = GetCategoryRange(category); if (!coordinateRange.HasData) { return; } else if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes); } double minimum = (double)coordinateRange.Minimum.Value; double maximum = (double)coordinateRange.Maximum.Value; IEnumerable barSeries = SeriesHost.Series.OfType().Where(series => series.ActualIndependentAxis == ActualIndependentAxis); int numberOfSeries = barSeries.Count(); double coordinateRangeHeight = (maximum - minimum); double segmentHeight = coordinateRangeHeight * 0.8; double barHeight = segmentHeight / numberOfSeries; int seriesIndex = barSeries.IndexOf(this); double dataPointX = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(dataPoint.ActualDependentValue)).Value; double zeroPointX = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value; double offset = seriesIndex * Math.Round(barHeight) + coordinateRangeHeight * 0.1; double dataPointY = minimum + offset; if (GetIsDataPointGrouped(category)) { // Multiple DataPoints share this category; offset and overlap them appropriately IGrouping categoryGrouping = GetDataPointGroup(category); int index = categoryGrouping.IndexOf(dataPoint); dataPointY += (index * (barHeight * 0.2)) / (categoryGrouping.Count() - 1); barHeight *= 0.8; Canvas.SetZIndex(dataPoint, -index); } if (ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(dataPointY) && ValueHelper.CanGraph(zeroPointX)) { dataPoint.Visibility = Visibility.Visible; double top = Math.Round(dataPointY); double height = Math.Round(barHeight); double left = Math.Round(Math.Min(dataPointX, zeroPointX) - 0.5); double right = Math.Round(Math.Max(dataPointX, zeroPointX) - 0.5); double width = right - left + 1; Canvas.SetLeft(dataPoint, left); Canvas.SetTop(dataPoint, top); dataPoint.Width = width; dataPoint.Height = height; } else { dataPoint.Visibility = Visibility.Collapsed; } } } } #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/BubbleSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Linq; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in X/Y /// line format. A third binding determines the size of the data point. /// /// Preview [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(BubbleDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] public class BubbleSeries : DataPointSingleSeriesWithAxes { /// /// The maximum bubble size as a ratio of the smallest dimension. /// private const double MaximumBubbleSizeAsRatioOfSmallestDimension = 0.25; /// /// The binding used to identify the size value. /// private Binding _sizeValueBinding; /// /// Gets or sets the Binding to use for identifying the size of the bubble. /// public Binding SizeValueBinding { get { return _sizeValueBinding; } set { if (_sizeValueBinding != value) { _sizeValueBinding = value; Refresh(); } } } /// /// Gets or sets the Binding Path to use for identifying the size of the bubble. /// public string SizeValuePath { get { return (null != SizeValueBinding) ? SizeValueBinding.Path.Path : null; } set { if (null == value) { SizeValueBinding = null; } else { SizeValueBinding = new Binding(value); } } } /// /// Stores the range of ActualSize values for the BubbleDataPoints. /// private Range _rangeOfActualSizeValues = new Range(); /// /// Initializes a new instance of the bubble series. /// public BubbleSeries() { } /// /// Creates a new instance of bubble data point. /// /// A new instance of bubble data point. protected override DataPoint CreateDataPoint() { return new BubbleDataPoint(); } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// protected override IEnumerator GetResourceDictionaryEnumeratorFromHost() { return GetResourceDictionaryWithTargetType(SeriesHost, typeof(BubbleDataPoint), true); } /// /// Acquire a horizontal linear axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// /// Prepares a bubble data point by binding the size value binding to /// the size property. /// /// The data point to prepare. /// The data context of the data point. /// protected override void PrepareDataPoint(DataPoint dataPoint, object dataContext) { base.PrepareDataPoint(dataPoint, dataContext); BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; bubbleDataPoint.SetBinding(BubbleDataPoint.SizeProperty, SizeValueBinding ?? DependentValueBinding ?? IndependentValueBinding); } /// /// Attaches size change and actual size change event handlers to the /// data point. /// /// The data point. protected override void AttachEventHandlersToDataPoint(DataPoint dataPoint) { BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; bubbleDataPoint.SizePropertyChanged += BubbleDataPointSizePropertyChanged; bubbleDataPoint.ActualSizePropertyChanged += BubbleDataPointActualSizePropertyChanged; base.AttachEventHandlersToDataPoint(dataPoint); } /// /// Detaches size change and actual size change event handlers from the /// data point. /// /// The data point. protected override void DetachEventHandlersFromDataPoint(DataPoint dataPoint) { BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; bubbleDataPoint.SizePropertyChanged -= BubbleDataPointSizePropertyChanged; bubbleDataPoint.ActualSizePropertyChanged -= BubbleDataPointActualSizePropertyChanged; base.DetachEventHandlersFromDataPoint(dataPoint); } /// /// Updates all data points when the actual size property of a data /// point changes. /// /// The source of the event. /// Information about the event. private void BubbleDataPointActualSizePropertyChanged(object sender, RoutedPropertyChangedEventArgs e) { Range newRangeOfActualSizeValues = ActiveDataPoints.OfType().Select(d => Math.Abs(d.ActualSize)).GetRange(); if (newRangeOfActualSizeValues == _rangeOfActualSizeValues) { // No range change - only need to update the current point UpdateDataPoint((BubbleDataPoint)sender); } else { // Range has changed - need to update all points UpdateDataPoints(ActiveDataPoints); } } /// /// Animates the value of the ActualSize property to the size property /// when it changes. /// /// The source of the event. /// Information about the event. private void BubbleDataPointSizePropertyChanged(object sender, RoutedPropertyChangedEventArgs e) { BubbleDataPoint dataPoint = (BubbleDataPoint)sender; DependencyPropertyAnimationHelper.BeginAnimation( dataPoint, BubbleDataPoint.ActualSizeProperty, "ActualSize", e.NewValue, TransitionDuration, this.TransitionEasingFunction); } /// /// Calculates the range of ActualSize values of all active BubbleDataPoints. /// protected override void OnBeforeUpdateDataPoints() { _rangeOfActualSizeValues = ActiveDataPoints.OfType().Select(d => Math.Abs(d.ActualSize)).GetRange(); } /// /// Ensure that if any data points are updated, all data points are /// updated. /// /// The data points to update. protected override void UpdateDataPoints(IEnumerable dataPoints) { base.UpdateDataPoints(ActiveDataPoints); } /// /// Updates the data point's visual representation. /// /// The data point. protected override void UpdateDataPoint(DataPoint dataPoint) { double maximumDiameter = Math.Min(PlotAreaSize.Width, PlotAreaSize.Height) * MaximumBubbleSizeAsRatioOfSmallestDimension; BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; double ratioOfLargestBubble = (_rangeOfActualSizeValues.HasData && _rangeOfActualSizeValues.Maximum != 0.0 && bubbleDataPoint.ActualSize >= 0.0) ? Math.Abs(bubbleDataPoint.ActualSize) / _rangeOfActualSizeValues.Maximum : 0.0; bubbleDataPoint.Width = ratioOfLargestBubble * maximumDiameter; bubbleDataPoint.Height = ratioOfLargestBubble * maximumDiameter; double left = (ActualIndependentAxis.GetPlotAreaCoordinate(bubbleDataPoint.ActualIndependentValue)).Value - (bubbleDataPoint.Width / 2.0); double top = (PlotAreaSize.Height - (bubbleDataPoint.Height / 2.0)) - ActualDependentRangeAxis.GetPlotAreaCoordinate(bubbleDataPoint.ActualDependentValue).Value; if (ValueHelper.CanGraph(left) && ValueHelper.CanGraph(top)) { dataPoint.Visibility = Visibility.Visible; Canvas.SetLeft(bubbleDataPoint, left); Canvas.SetTop(bubbleDataPoint, top); } else { dataPoint.Visibility = Visibility.Collapsed; } } /// /// Updates the value margins after all data points are updated. /// protected override void OnAfterUpdateDataPoints() { IValueMarginProvider provider = this as IValueMarginProvider; { IValueMarginConsumer consumer = ActualDependentRangeAxis as IValueMarginConsumer; if (consumer != null) { consumer.ValueMarginsChanged(provider, GetValueMargins(consumer)); } } { IValueMarginConsumer consumer = ActualIndependentAxis as IValueMarginConsumer; if (consumer != null) { consumer.ValueMarginsChanged(provider, GetValueMargins(consumer)); } } base.OnAfterUpdateDataPoints(); } /// /// Gets the dependent axis as a range axis. /// public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } #region public IRangeAxis DependentRangeAxis /// /// Gets or sets the dependent range axis. /// public IRangeAxis DependentRangeAxis { get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register( "DependentRangeAxis", typeof(IRangeAxis), typeof(BubbleSeries), new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); /// /// DependentRangeAxisProperty property changed handler. /// /// BubbleSeries that changed its DependentRangeAxis. /// Event arguments. private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { BubbleSeries source = (BubbleSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnDependentRangeAxisPropertyChanged(newValue); } /// /// DependentRangeAxisProperty property changed handler. /// /// New value. private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; } #endregion public IRangeAxis DependentRangeAxis /// /// Gets the independent axis as a range axis. /// public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } #region public IAxis IndependentAxis /// /// Gets or sets independent range axis. /// public IAxis IndependentAxis { get { return GetValue(IndependentAxisProperty) as IAxis; } set { SetValue(IndependentAxisProperty, value); } } /// /// Identifies the IndependentAxis dependency property. /// public static readonly DependencyProperty IndependentAxisProperty = DependencyProperty.Register( "IndependentAxis", typeof(IAxis), typeof(BubbleSeries), new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); /// /// IndependentAxisProperty property changed handler. /// /// BubbleSeries that changed its IndependentAxis. /// Event arguments. private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { BubbleSeries source = (BubbleSeries)d; IAxis newValue = (IAxis)e.NewValue; source.OnIndependentAxisPropertyChanged(newValue); } /// /// IndependentAxisProperty property changed handler. /// /// New value. private void OnIndependentAxisPropertyChanged(IAxis newValue) { this.InternalIndependentAxis = (IAxis)newValue; } #endregion public IAxis IndependentAxis /// /// The margins required for each value. /// /// The consumer to return the value margins for. /// A sequence of margins for each value. protected override IEnumerable GetValueMargins(IValueMarginConsumer consumer) { IAxis axis = consumer as IAxis; if (axis != null) { return ActiveDataPoints.Select(dataPoint => { double margin = dataPoint.GetMargin(axis); return new ValueMargin( GetActualDataPointAxisValue(dataPoint, axis), margin, margin); }); } return Enumerable.Empty(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/CandlestickSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; namespace System.Windows.Controls.DataVisualization.Charting { [StyleTypedProperty(Property = "DataPointStyle", StyleTargetType = typeof(CandlestickDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] public sealed partial class CandlestickSeries : DataPointSingleSeriesWithAxes { public Binding OpenValueBinding { get; set; } public Binding CloseValueBinding { get; set; } public Binding HighValueBinding { get; set; } public Binding LowValueBinding { get; set; } public CandlestickSeries() { } /// /// Gets the dependent axis as a range axis. /// public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } #region public IRangeAxis DependentRangeAxis /// /// Gets or sets the dependent range axis. /// public IRangeAxis DependentRangeAxis { get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register( "DependentRangeAxis", typeof(IRangeAxis), typeof(CandlestickSeries), new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); /// /// DependentRangeAxisProperty property changed handler. /// /// CandlestickSeries that changed its DependentRangeAxis. /// Event arguments. private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { CandlestickSeries source = (CandlestickSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnDependentRangeAxisPropertyChanged(newValue); } /// /// DependentRangeAxisProperty property changed handler. /// /// New value. private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; } #endregion public IRangeAxis DependentRangeAxis /// /// Gets the independent axis as a range axis. /// public IRangeAxis ActualIndependentRangeAxis { get { return this.InternalActualIndependentAxis as IRangeAxis; } } #region public IRangeAxis IndependentRangeAxis /// /// Gets or sets the independent range axis. /// public IRangeAxis IndependentRangeAxis { get { return GetValue(IndependentRangeAxisProperty) as IRangeAxis; } set { SetValue(IndependentRangeAxisProperty, value); } } /// /// Identifies the IndependentRangeAxis dependency property. /// public static readonly DependencyProperty IndependentRangeAxisProperty = DependencyProperty.Register( "IndependentRangeAxis", typeof(IRangeAxis), typeof(CandlestickSeries), new PropertyMetadata(null, OnIndependentRangeAxisPropertyChanged)); /// /// IndependentRangeAxisProperty property changed handler. /// /// CandlestickSeries that changed its IndependentRangeAxis. /// Event arguments. private static void OnIndependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { CandlestickSeries source = (CandlestickSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnIndependentRangeAxisPropertyChanged(newValue); } /// /// IndependentRangeAxisProperty property changed handler. /// /// New value. private void OnIndependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalIndependentAxis = (IAxis)newValue; } #endregion public IRangeAxis IndependentRangeAxis /// /// Acquire a horizontal linear axis and a vertical linear axis. /// /// The first data point. //protected override void GetAxes(DataPoint firstDataPoint) //{ // GetRangeAxis( // InternalIndependentAxis, // firstDataPoint, // AxisOrientation.Horizontal, // () => CreateRangeAxisFromData(firstDataPoint.IndependentValue), // () => InternalActualIndependentAxis as IRangeAxis, // (value) => { InternalActualIndependentAxis = (IAxis)value; }, // (dataPoint) => dataPoint.IndependentValue); // GetRangeAxis( // InternalDependentAxis, // firstDataPoint, // AxisOrientation.Vertical, // () => // { // HybridAxis axis = (HybridAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); // axis.ShowGridLines = true; // return (IRangeAxis)axis; // }, // () => InternalActualDependentAxis as IRangeAxis, // (value) => { InternalActualDependentAxis = (IAxis)value; }, // (dataPoint) => dataPoint.DependentValue); //} protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// protected override IEnumerator GetResourceDictionaryEnumeratorFromHost() { return GetResourceDictionaryWithTargetType(SeriesHost, typeof(CandlestickDataPoint), true); } protected override void PrepareDataPoint(DataPoint dataPoint, object dataContext) { base.PrepareDataPoint(dataPoint, dataContext); CandlestickDataPoint candlestickDataPoint = (CandlestickDataPoint)dataPoint; candlestickDataPoint.SetBinding(CandlestickDataPoint.OpenProperty, OpenValueBinding); candlestickDataPoint.SetBinding(CandlestickDataPoint.CloseProperty, CloseValueBinding); candlestickDataPoint.SetBinding(CandlestickDataPoint.HighProperty, HighValueBinding); candlestickDataPoint.SetBinding(CandlestickDataPoint.LowProperty, LowValueBinding); } /// /// Creates a new Candlestick data point. /// /// A Candlestick data point. protected override DataPoint CreateDataPoint() { return new CandlestickDataPoint(); } /// /// Returns the style to use for all data points. /// /// The style to use for all data points. //protected override Style GetDataPointStyleFromHost() //{ // return SeriesHost.NextStyle(typeof(CandlestickDataPoint), true); //} /// /// This method updates a single data point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { CandlestickDataPoint candlestickDataPoint = (CandlestickDataPoint)dataPoint; double PlotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; double dataPointX = ActualIndependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value; double highPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(candlestickDataPoint.High)).Value; double lowPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(candlestickDataPoint.Low)).Value; double openPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(candlestickDataPoint.Open)).Value; double closePointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(candlestickDataPoint.Close)).Value; candlestickDataPoint.UpdateBody(ActualDependentRangeAxis); if (ValueHelper.CanGraph(dataPointX)) { dataPoint.Height = Math.Abs(highPointY - lowPointY); dataPoint.Width = 5.0; if (dataPoint.ActualWidth == 0.0 || dataPoint.ActualHeight == 0.0) dataPoint.UpdateLayout(); Canvas.SetLeft(dataPoint, Math.Round(dataPointX - (dataPoint.ActualWidth / 2))); Canvas.SetTop(dataPoint, Math.Round(PlotAreaHeight - highPointY)); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/ColumnBarBaseSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace System.Windows.Controls.DataVisualization.Charting { /// /// This series serves as the base class for the column and bar series. /// /// The type of the data point. public abstract class ColumnBarBaseSeries : DataPointSingleSeriesWithAxes, IAnchoredToOrigin where T : DataPoint, new() { #region public IRangeAxis DependentRangeAxis /// /// Gets or sets the dependent range axis. /// public IRangeAxis DependentRangeAxis { get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register( "DependentRangeAxis", typeof(IRangeAxis), typeof(ColumnBarBaseSeries), new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); /// /// DependentRangeAxisProperty property changed handler. /// /// ColumnBarBaseSeries that changed its DependentRangeAxis. /// Event arguments. private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ColumnBarBaseSeries source = (ColumnBarBaseSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnDependentRangeAxisPropertyChanged(newValue); } /// /// DependentRangeAxisProperty property changed handler. /// /// New value. private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { InternalDependentAxis = (IAxis)newValue; } #endregion public IRangeAxis DependentRangeAxis #region public IAxis IndependentAxis /// /// Gets or sets the independent category axis. /// public IAxis IndependentAxis { get { return GetValue(IndependentAxisProperty) as IAxis; } set { SetValue(IndependentAxisProperty, value); } } /// /// Identifies the IndependentAxis dependency property. /// [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] public static readonly DependencyProperty IndependentAxisProperty = DependencyProperty.Register( "IndependentAxis", typeof(IAxis), typeof(ColumnBarBaseSeries), new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); /// /// IndependentAxisProperty property changed handler. /// /// ColumnBarBaseSeries that changed its IndependentAxis. /// Event arguments. private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ColumnBarBaseSeries source = (ColumnBarBaseSeries)d; IAxis newValue = (IAxis)e.NewValue; source.OnIndependentAxisPropertyChanged(newValue); } /// /// IndependentAxisProperty property changed handler. /// /// New value. private void OnIndependentAxisPropertyChanged(IAxis newValue) { InternalIndependentAxis = (IAxis)newValue; } #endregion public IAxis IndependentAxis /// /// Keeps a list of DataPoints that share the same category. /// private IDictionary> _categoriesWithMultipleDataPoints; /// /// Returns the group of data points in a given category. /// /// The category for which to return the data /// point group. /// The group of data points in a given category. protected IGrouping GetDataPointGroup(object category) { return _categoriesWithMultipleDataPoints[category]; } /// /// Returns a value indicating whether a data point corresponding to /// a category is grouped. /// /// The category. /// A value indicating whether a data point corresponding to /// a category is grouped. protected bool GetIsDataPointGrouped(object category) { return _categoriesWithMultipleDataPoints.ContainsKey(category); } /// /// The length of each data point. /// private double? _dataPointlength; /// /// Gets the dependent axis as a range axis. /// public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } /// /// Gets the independent axis as a category axis. /// public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis; } } /// /// Initializes a new instance of the ColumnBarBaseSeries class. /// protected ColumnBarBaseSeries() { } /// /// Method run before DataPoints are updated. /// protected override void OnBeforeUpdateDataPoints() { base.OnBeforeUpdateDataPoints(); CalculateDataPointLength(); // Update the list of DataPoints with the same category _categoriesWithMultipleDataPoints = ActiveDataPoints .Where(point => null != point.IndependentValue) .OrderBy(point => point.DependentValue) .GroupBy(point => point.IndependentValue) .Where(grouping => 1 < grouping.Count()) .ToDictionary(grouping => grouping.Key); } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// protected override IEnumerator GetResourceDictionaryEnumeratorFromHost() { return GetResourceDictionaryWithTargetType(SeriesHost, typeof(T), true); } /// /// Updates a data point when its actual dependent value has changed. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointActualDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue) { UpdateDataPoint(dataPoint); base.OnDataPointActualDependentValueChanged(dataPoint, oldValue, newValue); } /// /// Redraws other column series to assure they allocate the right amount /// of space for their columns. /// /// The series host to update. protected void RedrawOtherSeries(ISeriesHost seriesHost) { Type thisType = typeof(ColumnBarBaseSeries); // redraw all other column series to ensure they make space for new one foreach (ColumnBarBaseSeries series in seriesHost.Series.Where(series => thisType.IsAssignableFrom(series.GetType())).OfType>().Where(series => series != this)) { series.UpdateDataPoints(series.ActiveDataPoints); } } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected override void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { base.OnDataPointsChanged(newDataPoints, oldDataPoints); CalculateDataPointLength(); if (this.SeriesHost != null) { RedrawOtherSeries(this.SeriesHost); } } /// /// Redraw other column series when removed from a series host. /// /// The old value of the series host property. /// The new value of the series host property. protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { base.OnSeriesHostPropertyChanged(oldValue, newValue); // If being removed from series host, redraw all column series. if (newValue == null || oldValue != null) { RedrawOtherSeries(oldValue); } } /// /// Creates the bar data point. /// /// A bar data point. protected override DataPoint CreateDataPoint() { return new T(); } /// /// Calculates the length of the data points. /// protected void CalculateDataPointLength() { if (!(ActualIndependentAxis is ICategoryAxis)) { IEnumerable values = ActiveDataPoints .Select(dataPoint => ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue)) .Where(value => ValueHelper.CanGraph(value.Value)) .OrderBy(value => value.Value) .ToList(); _dataPointlength = EnumerableFunctions.Zip( values, values.Skip(1), (left, right) => new Range(left.Value, right.Value)) .Select(range => range.Maximum - range.Minimum) .MinOrNullable(); } } /// /// Returns the value margins for a given axis. /// /// The axis to retrieve the value margins for. /// /// A sequence of value margins. protected override IEnumerable GetValueMargins(IValueMarginConsumer consumer) { double dependentValueMargin = this.ActualHeight / 10; IAxis axis = consumer as IAxis; if (axis != null && ActiveDataPoints.Any()) { Func selector = null; if (axis == InternalActualIndependentAxis) { selector = (dataPoint) => (IComparable)dataPoint.ActualIndependentValue; DataPoint minimumPoint = ActiveDataPoints.MinOrNull(selector); DataPoint maximumPoint = ActiveDataPoints.MaxOrNull(selector); double minimumMargin = minimumPoint.GetMargin(axis); yield return new ValueMargin(selector(minimumPoint), minimumMargin, minimumMargin); double maximumMargin = maximumPoint.GetMargin(axis); yield return new ValueMargin(selector(maximumPoint), maximumMargin, maximumMargin); } else if (axis == InternalActualDependentAxis) { selector = (dataPoint) => (IComparable)dataPoint.ActualDependentValue; DataPoint minimumPoint = ActiveDataPoints.MinOrNull(selector); DataPoint maximumPoint = ActiveDataPoints.MaxOrNull(selector); yield return new ValueMargin(selector(minimumPoint), dependentValueMargin, dependentValueMargin); yield return new ValueMargin(selector(maximumPoint), dependentValueMargin, dependentValueMargin); } } else { yield break; } } /// /// Gets a range in which to render a data point. /// /// The category to retrieve the range for. /// /// The range in which to render a data point. protected Range GetCategoryRange(object category) { ICategoryAxis categoryAxis = ActualIndependentAxis as CategoryAxis; if (categoryAxis != null) { return categoryAxis.GetPlotAreaCoordinateRange(category); } else { UnitValue unitValue = ActualIndependentAxis.GetPlotAreaCoordinate(category); if (ValueHelper.CanGraph(unitValue.Value) && _dataPointlength.HasValue) { double halfLength = _dataPointlength.Value / 2.0; return new Range( new UnitValue(unitValue.Value - halfLength, unitValue.Unit), new UnitValue(unitValue.Value + halfLength, unitValue.Unit)); } return new Range(); } } /// /// Gets the axis to which the data is anchored. /// IRangeAxis IAnchoredToOrigin.AnchoredAxis { get { return this.ActualDependentRangeAxis; } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/ColumnSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in column format. /// /// Preview [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ColumnDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] public partial class ColumnSeries : ColumnBarBaseSeries { /// /// Initializes a new instance of the ColumnSeries class. /// public ColumnSeries() { } /// /// Acquire a horizontal category axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => new CategoryAxis { Orientation = AxisOrientation.X }, (axis) => { IRangeAxis rangeAxis = axis as IRangeAxis; return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.Y; }, () => { IRangeAxis rangeAxis = CreateRangeAxisFromData(firstDataPoint.DependentValue); rangeAxis.Orientation = AxisOrientation.Y; if (rangeAxis == null || rangeAxis.Origin == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } DisplayAxis axis = rangeAxis as DisplayAxis; if (axis != null) { axis.ShowGridLines = true; } return rangeAxis; }); } /// /// Updates each point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { if (SeriesHost == null || PlotArea == null) { return; } object category = dataPoint.ActualIndependentValue ?? (this.ActiveDataPoints.IndexOf(dataPoint) + 1); Range coordinateRange = GetCategoryRange(category); if (!coordinateRange.HasData) { return; } else if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes); } double minimum = (double)coordinateRange.Minimum.Value; double maximum = (double)coordinateRange.Maximum.Value; double plotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; IEnumerable columnSeries = SeriesHost.Series.OfType().Where(series => series.ActualIndependentAxis == ActualIndependentAxis); int numberOfSeries = columnSeries.Count(); double coordinateRangeWidth = (maximum - minimum); double segmentWidth = coordinateRangeWidth * 0.8; double columnWidth = segmentWidth / numberOfSeries; int seriesIndex = columnSeries.IndexOf(this); double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(dataPoint.ActualDependentValue)).Value; double zeroPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value; double offset = seriesIndex * Math.Round(columnWidth) + coordinateRangeWidth * 0.1; double dataPointX = minimum + offset; if (GetIsDataPointGrouped(category)) { // Multiple DataPoints share this category; offset and overlap them appropriately IGrouping categoryGrouping = GetDataPointGroup(category); int index = categoryGrouping.IndexOf(dataPoint); dataPointX += (index * (columnWidth * 0.2)) / (categoryGrouping.Count() - 1); columnWidth *= 0.8; Canvas.SetZIndex(dataPoint, -index); } if (ValueHelper.CanGraph(dataPointY) && ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(zeroPointY)) { dataPoint.Visibility = Visibility.Visible; double left = Math.Round(dataPointX); double width = Math.Round(columnWidth); double top = Math.Round(plotAreaHeight - Math.Max(dataPointY, zeroPointY) + 0.5); double bottom = Math.Round(plotAreaHeight - Math.Min(dataPointY, zeroPointY) + 0.5); double height = bottom - top + 1; Canvas.SetLeft(dataPoint, left); Canvas.SetTop(dataPoint, top); dataPoint.Width = width; dataPoint.Height = height; } else { dataPoint.Visibility = Visibility.Collapsed; } } } } #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Compatible/AreaSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Linq; using System.Windows.Data; using System.Windows.Media.Animation; using System.Windows.Shapes; #if DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting #else namespace System.Windows.Controls.DataVisualization.Charting.Compatible #endif { /// /// Control that displays values as an area chart visualization. /// /// /// Based on the DefinitionSeries hierarchy. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(AreaDataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = PathStyleName, StyleTargetType = typeof(Path))] public class AreaSeries : StackedAreaSeries { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Name of the PathStyle property. /// private const string PathStyleName = "PathStyle"; /// /// Field storing the single SeriesDefinition used by the series. /// private SeriesDefinition _definition; /// /// Initializes a new instance of the AreaSeries class. /// public AreaSeries() { SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); _definition = new SeriesDefinition(); _definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); _definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); _definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.DataShapeStyleProperty, new Binding(PathStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); #if !NO_EASING_FUNCTIONS _definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); #endif // For compatibility DependentValueBinding = new Binding(); IndependentValueBinding = new Binding(); SeriesDefinitions.Add(_definition); } /// /// Gets a sequence of IndependentValueGroups. /// protected override IEnumerable IndependentValueGroups { get { // Base implementation groups by independent value; when plotting a single series in isolation, that's not desirable return DataItems .Select(di => new IndependentValueGroup(di.ActualIndependentValue, new DataItem[] { di })); } } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(AreaSeries), null); /// /// Gets or sets the Binding that identifies the dependent values of the series. /// public Binding DependentValueBinding { get { return _definition.DependentValueBinding; } set { _definition.DependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the dependent values of the series. /// public string DependentValuePath { get { return _definition.DependentValuePath; } set { _definition.DependentValuePath = value; } } /// /// Gets or sets the Binding that identifies the independent values of the series. /// public Binding IndependentValueBinding { get { return _definition.IndependentValueBinding; } set { _definition.IndependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the independent values of the series. /// public string IndependentValuePath { get { return _definition.IndependentValuePath; } set { _definition.IndependentValuePath = value; } } /// /// Gets or sets the IRangeAxis to use as the dependent axis of the series. /// public IRangeAxis DependentRangeAxis { get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(AreaSeries), null); /// /// Gets or sets the title of the series. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(AreaSeries), null); /// /// Gets or sets the Style to use for the DataPoints of the series. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(AreaSeries), null); /// /// Gets or sets the Style to use for the LegendItem of the series. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(AreaSeries), null); /// /// Gets or sets a value indicating whether selection is enabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(AreaSeries), null); /// /// Gets or sets the Style to use for the Path of the series. /// public Style PathStyle { get { return (Style)GetValue(PathStyleProperty); } set { SetValue(PathStyleProperty, value); } } /// /// Identifies the PathStyle dependency property. /// public static readonly DependencyProperty PathStyleProperty = DependencyProperty.Register(PathStyleName, typeof(Style), typeof(AreaSeries), null); /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(AreaSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(AreaSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Compatible/BarSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Windows.Data; using System.Windows.Media.Animation; #if DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting #else namespace System.Windows.Controls.DataVisualization.Charting.Compatible #endif { /// /// Control that displays values as a bar chart visualization. /// /// /// Based on the DefinitionSeries hierarchy. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(BarDataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] public class BarSeries : StackedBarSeries { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Field storing the single SeriesDefinition used by the series. /// private SeriesDefinition _definition; /// /// Initializes a new instance of the BarSeries class. /// public BarSeries() { SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); _definition = new SeriesDefinition(); _definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); _definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); _definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); #if !NO_EASING_FUNCTIONS _definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); #endif // For compatibility DependentValueBinding = new Binding(); IndependentValueBinding = new Binding(); SeriesDefinitions.Add(_definition); } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(BarSeries), null); /// /// Gets or sets the Binding that identifies the dependent values of the series. /// public Binding DependentValueBinding { get { return _definition.DependentValueBinding; } set { _definition.DependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the dependent values of the series. /// public string DependentValuePath { get { return _definition.DependentValuePath; } set { _definition.DependentValuePath = value; } } /// /// Gets or sets the Binding that identifies the independent values of the series. /// public Binding IndependentValueBinding { get { return _definition.IndependentValueBinding; } set { _definition.IndependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the independent values of the series. /// public string IndependentValuePath { get { return _definition.IndependentValuePath; } set { _definition.IndependentValuePath = value; } } /// /// Gets or sets the IRangeAxis to use as the dependent axis of the series. /// public IRangeAxis DependentRangeAxis { get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(BarSeries), null); /// /// Gets or sets the title of the series. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(BarSeries), null); /// /// Gets or sets the Style to use for the DataPoints of the series. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(BarSeries), null); /// /// Gets or sets the Style to use for the LegendItem of the series. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(BarSeries), null); /// /// Gets or sets a value indicating whether selection is enabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(BarSeries), null); /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(BarSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(BarSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Compatible/ColumnSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Windows.Data; using System.Windows.Media.Animation; #if DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting #else namespace System.Windows.Controls.DataVisualization.Charting.Compatible #endif { /// /// Control that displays values as a column chart visualization. /// /// /// Based on the DefinitionSeries hierarchy. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ColumnDataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] public class ColumnSeries : StackedColumnSeries { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Field storing the single SeriesDefinition used by the series. /// private SeriesDefinition _definition; /// /// Initializes a new instance of the ColumnSeries class. /// public ColumnSeries() { SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); _definition = new SeriesDefinition(); _definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); _definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); _definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); #if !NO_EASING_FUNCTIONS _definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); #endif // For compatibility DependentValueBinding = new Binding(); IndependentValueBinding = new Binding(); SeriesDefinitions.Add(_definition); } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ColumnSeries), null); /// /// Gets or sets the Binding that identifies the dependent values of the series. /// public Binding DependentValueBinding { get { return _definition.DependentValueBinding; } set { _definition.DependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the dependent values of the series. /// public string DependentValuePath { get { return _definition.DependentValuePath; } set { _definition.DependentValuePath = value; } } /// /// Gets or sets the Binding that identifies the independent values of the series. /// public Binding IndependentValueBinding { get { return _definition.IndependentValueBinding; } set { _definition.IndependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the independent values of the series. /// public string IndependentValuePath { get { return _definition.IndependentValuePath; } set { _definition.IndependentValuePath = value; } } /// /// Gets or sets the IRangeAxis to use as the dependent axis of the series. /// public IRangeAxis DependentRangeAxis { get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(ColumnSeries), null); /// /// Gets or sets the title of the series. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(ColumnSeries), null); /// /// Gets or sets the Style to use for the DataPoints of the series. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(ColumnSeries), null); /// /// Gets or sets the Style to use for the LegendItem of the series. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(ColumnSeries), null); /// /// Gets or sets a value indicating whether selection is enabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(ColumnSeries), null); /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(ColumnSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(ColumnSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Compatible/LineSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Data; using System.Windows.Media.Animation; using System.Windows.Shapes; #if DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting #else namespace System.Windows.Controls.DataVisualization.Charting.Compatible #endif { /// /// Control that displays values as an line chart visualization. /// /// /// Based on the DefinitionSeries hierarchy. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(LineDataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = PolylineStyleName, StyleTargetType = typeof(Polyline))] public class LineSeries : StackedLineSeries { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Name of the PolylineStyle property. /// private const string PolylineStyleName = "PolylineStyle"; /// /// Field storing the single SeriesDefinition used by the series. /// private SeriesDefinition _definition; /// /// Initializes a new instance of the LineSeries class. /// public LineSeries() { SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); _definition = new SeriesDefinition(); _definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); _definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); _definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.DataShapeStyleProperty, new Binding(PolylineStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); #if !NO_EASING_FUNCTIONS _definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); #endif // For compatibility DependentValueBinding = new Binding(); IndependentValueBinding = new Binding(); SeriesDefinitions.Add(_definition); } /// /// Gets a sequence of IndependentValueGroups. /// protected override IEnumerable IndependentValueGroups { get { // Base implementation groups by independent value; when plotting a single series in isolation, that's not desirable return DataItems .Select(di => new IndependentValueGroup(di.ActualIndependentValue, new DataItem[] { di })); } } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(LineSeries), null); /// /// Gets or sets the Binding that identifies the dependent values of the series. /// public Binding DependentValueBinding { get { return _definition.DependentValueBinding; } set { _definition.DependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the dependent values of the series. /// public string DependentValuePath { get { return _definition.DependentValuePath; } set { _definition.DependentValuePath = value; } } /// /// Gets or sets the Binding that identifies the independent values of the series. /// public Binding IndependentValueBinding { get { return _definition.IndependentValueBinding; } set { _definition.IndependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the independent values of the series. /// public string IndependentValuePath { get { return _definition.IndependentValuePath; } set { _definition.IndependentValuePath = value; } } /// /// Gets or sets the IRangeAxis to use as the dependent axis of the series. /// public IRangeAxis DependentRangeAxis { get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(LineSeries), null); /// /// Gets or sets the title of the series. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(LineSeries), null); /// /// Gets or sets the Style to use for the DataPoints of the series. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(LineSeries), null); /// /// Gets or sets the Style to use for the LegendItem of the series. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(LineSeries), null); /// /// Gets or sets a value indicating whether selection is enabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(LineSeries), null); /// /// Gets or sets the Style to use for the Path of the series. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.Compatible.LineSeries.#PolylineStyle", Justification = "Matches spelling of same-named framework class.")] public Style PolylineStyle { get { return (Style)GetValue(PolylineStyleProperty); } set { SetValue(PolylineStyleProperty, value); } } /// /// Identifies the PolylineStyle dependency property. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches spelling of same-named framework class.")] public static readonly DependencyProperty PolylineStyleProperty = DependencyProperty.Register(PolylineStyleName, typeof(Style), typeof(LineSeries), null); /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(LineSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(LineSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Compatible/ScatterSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Linq; using System.Windows.Data; using System.Windows.Media.Animation; #if DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting #else namespace System.Windows.Controls.DataVisualization.Charting.Compatible #endif { /// /// Control that displays values as a scatter chart visualization. /// /// /// Based on the DefinitionSeries hierarchy. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ScatterDataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] public class ScatterSeries : StackedLineSeries { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Field storing the single SeriesDefinition used by the series. /// private SeriesDefinition _definition; /// /// Initializes a new instance of the ScatterSeries class. /// public ScatterSeries() { SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); _definition = new SeriesDefinition(); _definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); _definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); _definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); _definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); #if !NO_EASING_FUNCTIONS _definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); #endif // For compatibility DependentValueBinding = new Binding(); IndependentValueBinding = new Binding(); SeriesDefinitions.Add(_definition); } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected override DataPoint CreateDataPoint() { return new ScatterDataPoint(); } /// /// Updates the shape for the series. /// /// Locations of the points of each SeriesDefinition in the series. protected override void UpdateShape(IList> definitionPoints) { // Do not call base class implementation; leave shape empty for an easy way to use StackedLineSeries for ScatterSeries } /// /// Gets a sequence of IndependentValueGroups. /// protected override IEnumerable IndependentValueGroups { get { // Base implementation groups by independent value; when plotting a single series in isolation, that's not desirable return DataItems .Select(di => new IndependentValueGroup(di.ActualIndependentValue, new DataItem[] { di })); } } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ScatterSeries), null); /// /// Gets or sets the Binding that identifies the dependent values of the series. /// public Binding DependentValueBinding { get { return _definition.DependentValueBinding; } set { _definition.DependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the dependent values of the series. /// public string DependentValuePath { get { return _definition.DependentValuePath; } set { _definition.DependentValuePath = value; } } /// /// Gets or sets the Binding that identifies the independent values of the series. /// public Binding IndependentValueBinding { get { return _definition.IndependentValueBinding; } set { _definition.IndependentValueBinding = value; } } /// /// Gets or sets the Binding path that identifies the independent values of the series. /// public string IndependentValuePath { get { return _definition.IndependentValuePath; } set { _definition.IndependentValuePath = value; } } /// /// Gets or sets the IRangeAxis to use as the dependent axis of the series. /// public IRangeAxis DependentRangeAxis { get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(ScatterSeries), null); /// /// Gets or sets the title of the series. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(ScatterSeries), null); /// /// Gets or sets the Style to use for the DataPoints of the series. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(ScatterSeries), null); /// /// Gets or sets the Style to use for the LegendItem of the series. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(ScatterSeries), null); /// /// Gets or sets a value indicating whether selection is enabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(ScatterSeries), null); /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(ScatterSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(ScatterSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Compatible/SelectionEnabledToSelectionModeConverter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Globalization; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting.Compatible { /// /// Converts from a true/false value indicating whether selection is enabled to a SeriesSelectionMode. /// internal class SelectionEnabledToSelectionModeConverter : IValueConverter { /// /// Initializes a new instance of the SelectionEnabledToSelectionModeConverter class. /// public SelectionEnabledToSelectionModeConverter() { } /// /// Converts a value. /// /// The value produced by the binding source. /// The type of the binding target property. /// The converter parameter to use. /// The culture to use in the converter. /// Converted value. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { SeriesSelectionMode selectionMode = SeriesSelectionMode.None; if ((value is bool) && (bool)value) { selectionMode = SeriesSelectionMode.Single; } return selectionMode; } /// /// Converts a value back. /// /// The value produced by the binding source. /// The type of the binding target property. /// The converter parameter to use. /// The culture to use in the converter. /// Converted value. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/DataPointSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Windows.Controls.DataVisualization.Collections; using System.Windows.Data; using System.Windows.Media; using System.Windows.Media.Animation; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a dynamic data series. /// /// Preview public abstract partial class DataPointSeries : Series { /// /// The name of the template part with the plot area. /// protected const string PlotAreaName = "PlotArea"; /// /// The name of the DataPointStyle property and ResourceDictionary entry. /// protected const string DataPointStyleName = "DataPointStyle"; /// /// The name of the LegendItemStyle property and ResourceDictionary entry. /// protected const string LegendItemStyleName = "LegendItemStyle"; /// /// The name of the ActualLegendItemStyle property. /// protected internal const string ActualLegendItemStyleName = "ActualLegendItemStyle"; /// /// Event that is raised when selection is changed. /// public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent( "SelectionChanged", RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(DataPointSeries)); /// /// Queue of hide/reveal storyboards to play. /// private StoryboardQueue _storyBoardQueue = new StoryboardQueue(); /// /// The binding used to identify the dependent value binding. /// private Binding _dependentValueBinding; /// /// Gets or sets the Binding to use for identifying the dependent value. /// public Binding DependentValueBinding { get { return _dependentValueBinding; } set { if (value != _dependentValueBinding) { _dependentValueBinding = value; Refresh(); } } } /// /// Data points collection sorted by object. /// private MultipleDictionary _dataPointsByObject = new MultipleDictionary( true, new GenericEqualityComparer( (left, right) => left.Equals(right), (obj) => obj.GetHashCode()), new GenericEqualityComparer( (left, right) => object.ReferenceEquals(left, right), (obj) => obj.GetHashCode())); /// /// Gets or sets the Binding Path to use for identifying the dependent value. /// public string DependentValuePath { get { return (null != DependentValueBinding) ? DependentValueBinding.Path.Path : null; } set { if (null == value) { DependentValueBinding = null; } else { DependentValueBinding = new Binding(value); } } } /// /// The binding used to identify the independent value binding. /// private Binding _independentValueBinding; /// /// Gets or sets the Binding to use for identifying the independent value. /// public Binding IndependentValueBinding { get { return _independentValueBinding; } set { if (_independentValueBinding != value) { _independentValueBinding = value; Refresh(); } } } /// /// Gets or sets the Binding Path to use for identifying the independent value. /// public string IndependentValuePath { get { return (null != IndependentValueBinding) ? IndependentValueBinding.Path.Path : null; } set { if (null == value) { IndependentValueBinding = null; } else { IndependentValueBinding = new Binding(value); } } } #region public IEnumerable ItemsSource /// /// Gets or sets a collection used to contain the data points of the Series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( "ItemsSource", typeof(IEnumerable), typeof(DataPointSeries), new PropertyMetadata(OnItemsSourceChanged)); /// /// ItemsSourceProperty property changed callback. /// /// Series for which the ItemsSource changed. /// Event arguments. private static void OnItemsSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((DataPointSeries)o).OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue); } /// /// Called when the ItemsSource property changes. /// /// Old value of the ItemsSource property. /// New value of the ItemsSource property. protected virtual void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { // Remove handler for oldValue.CollectionChanged (if present) INotifyCollectionChanged oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged; if (null != oldValueINotifyCollectionChanged) { // Detach the WeakEventListener if (null != _weakEventListener) { _weakEventListener.Detach(); _weakEventListener = null; } } // Add handler for newValue.CollectionChanged (if possible) INotifyCollectionChanged newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged; if (null != newValueINotifyCollectionChanged) { // Use a WeakEventListener so that the backwards reference doesn't keep this object alive _weakEventListener = new WeakEventListener(this); _weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs); _weakEventListener.OnDetachAction = (weakEventListener) => newValueINotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent; newValueINotifyCollectionChanged.CollectionChanged += _weakEventListener.OnEvent; } if (TemplateApplied) { Refresh(); } } #endregion public IEnumerable ItemsSource #region public AnimationSequence AnimationSequence /// /// Gets or sets the animation sequence to use for the DataPoints of the Series. /// public AnimationSequence AnimationSequence { get { return (AnimationSequence)GetValue(AnimationSequenceProperty); } set { SetValue(AnimationSequenceProperty, value); } } /// /// Gets a stream of the active data points in the plot area. /// protected virtual IEnumerable ActiveDataPoints { get { return (null != PlotArea) ? PlotArea.Children.OfType().Where(dataPoint => dataPoint.IsActive) : Enumerable.Empty(); } } /// /// Gets the number of active data points in the plot area. /// protected int ActiveDataPointCount { get; private set; } #region public bool IsSelectionEnabled #region public IEasingFunction TransitionEasingFunction /// /// Gets or sets the easing function to use when transitioning the /// data points. /// #if !NO_EASING_FUNCTIONS public IEasingFunction TransitionEasingFunction { get { return GetValue(TransitionEasingFunctionProperty) as IEasingFunction; } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register( "TransitionEasingFunction", typeof(IEasingFunction), typeof(DataPointSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else internal IEasingFunction TransitionEasingFunction { get; set; } #endif #endregion public IEasingFunction TransitionEasingFunction /// /// Gets or sets a value indicating whether elements in the series can /// be selected. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } /// /// Identifies the IsSelectionEnabled dependency property. /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register( "IsSelectionEnabled", typeof(bool), typeof(DataPointSeries), new PropertyMetadata(false, OnIsSelectionEnabledPropertyChanged)); /// /// IsSelectionEnabledProperty property changed handler. /// /// DynamicSeries that changed its IsSelectionEnabled. /// /// Event arguments. private static void OnIsSelectionEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPointSeries source = (DataPointSeries)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsSelectionEnabledPropertyChanged(oldValue, newValue); } /// /// IsSelectionEnabledProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsSelectionEnabledPropertyChanged(bool oldValue, bool newValue) { foreach (DataPoint dataPoint in ActiveDataPoints) { dataPoint.IsSelectionEnabled = newValue; } } #endregion public bool IsSelectionEnabled /// /// Identifies the AnimationSequence dependency property. /// public static readonly DependencyProperty AnimationSequenceProperty = DependencyProperty.Register( "AnimationSequence", typeof(AnimationSequence), typeof(DataPointSeries), new PropertyMetadata(AnimationSequence.Simultaneous)); #endregion public AnimationSequence AnimationSequence /// /// WeakEventListener used to handle INotifyCollectionChanged events. /// private WeakEventListener _weakEventListener; /// /// The plot area canvas. /// private Panel _plotArea; /// /// Gets the plot area canvas. /// internal Panel PlotArea { get { return _plotArea; } private set { Panel oldValue = _plotArea; _plotArea = value; if (_plotArea != oldValue) { OnPlotAreaChanged(oldValue, value); } } } /// /// Gets the size of the plot area. /// /// /// Use this method instead of PlotArea.ActualWidth/ActualHeight /// because the ActualWidth and ActualHeight properties are set after /// the SizeChanged handler runs. /// protected Size PlotAreaSize { get; private set; } /// /// Event raised when selection has changed. /// public event SelectionChangedEventHandler SelectionChanged { add { AddHandler(SelectionChangedEvent, value); } remove { RemoveHandler(SelectionChangedEvent, value); } } /// /// Tracks whether a call to OnSelectedItemPropertyChanged is already in progress. /// private bool _processingOnSelectedItemPropertyChanged; #region public object SelectedItem /// /// Gets or sets the selected item. /// public object SelectedItem { get { return GetValue(SelectedItemProperty) as object; } set { SetValue(SelectedItemProperty, value); } } /// /// Identifies the SelectedItem dependency property. /// public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( "SelectedItem", typeof(object), typeof(DataPointSeries), new PropertyMetadata(null, OnSelectedItemPropertyChanged)); /// /// Called when the value of the SelectedItem property changes. /// /// DynamicSeries that changed its SelectedItem. /// Event arguments. private static void OnSelectedItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPointSeries source = (DataPointSeries)d; object oldValue = (object)e.OldValue; object newValue = (object)e.NewValue; source.OnSelectedItemPropertyChanged(oldValue, newValue); } /// /// Called when the value of the SelectedItem property changes. /// /// The old selected index. /// The new value. protected virtual void OnSelectedItemPropertyChanged(object oldValue, object newValue) { DataPoint dataPoint = null; if (null != newValue) { // Find the corresponding Control dataPoint = _dataPointsByObject[newValue].Where(dp => object.Equals(newValue, dp.DataContext) && dp.IsActive).FirstOrDefault(); if (null == dataPoint) { // None; clear SelectedItem try { _processingOnSelectedItemPropertyChanged = true; SelectedItem = null; // Clear newValue so the SelectionChanged event will be correct (or suppressed) newValue = null; } finally { _processingOnSelectedItemPropertyChanged = false; } } } // Unselect everything else foreach (DataPoint dataPointUnselect in ActiveDataPoints.Where(activeDataPoint => (activeDataPoint != dataPoint) && activeDataPoint.IsSelected)) { dataPointUnselect.IsSelectedChanged -= OnDataPointIsSelectedChanged; dataPointUnselect.IsSelected = false; dataPointUnselect.IsSelectedChanged += OnDataPointIsSelectedChanged; } if ((null != dataPoint) && !dataPoint.IsSelected) { // Select the new data point dataPoint.IsSelectedChanged -= OnDataPointIsSelectedChanged; dataPoint.IsSelected = true; dataPoint.IsSelectedChanged += OnDataPointIsSelectedChanged; } // Fire SelectionChanged (if appropriate) if (!_processingOnSelectedItemPropertyChanged && (oldValue != newValue)) { IList oldValues = new List(); if (oldValue != null) { oldValues.Add(oldValue); } IList newValues = new List(); if (newValue != null) { newValues.Add(newValue); } RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, oldValues, newValues)); } } #endregion public object SelectedItem /// /// Gets or sets a value indicating whether the template has been /// applied. /// private bool TemplateApplied { get; set; } #region public Style DataPointStyle /// /// Gets or sets the style to use for the data points. /// public Style DataPointStyle { get { return GetValue(DataPointStyleProperty) as Style; } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register( DataPointStyleName, typeof(Style), typeof(DataPointSeries), new PropertyMetadata(null, OnDataPointStylePropertyChanged)); /// /// DataPointStyleProperty property changed handler. /// /// DataPointSingleSeriesWithAxes that changed its DataPointStyle. /// Event arguments. private static void OnDataPointStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((DataPointSeries)d).OnDataPointStylePropertyChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// DataPointStyleProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnDataPointStylePropertyChanged(Style oldValue, Style newValue) { foreach (LegendItem legendItem in LegendItems.OfType()) { // Silverlight requires the following to pick up the new Style for the LegendItem marker object dataContext = legendItem.DataContext; legendItem.DataContext = null; legendItem.DataContext = dataContext; } } #endregion public Style DataPointStyle #region public Style LegendItemStyle /// /// Gets or sets the style to use for the legend items. /// public Style LegendItemStyle { get { return GetValue(LegendItemStyleProperty) as Style; } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register( LegendItemStyleName, typeof(Style), typeof(DataPointSeries), new PropertyMetadata(null, OnLegendItemStylePropertyChanged)); /// /// LegendItemStyleProperty property changed handler. /// /// DataPointSeries that changed its LegendItemStyle. /// Event arguments. private static void OnLegendItemStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPointSeries source = (DataPointSeries)d; source.OnLegendItemStylePropertyChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Called when the value of the LegendItemStyle property changes. /// /// Old value. /// New value. protected virtual void OnLegendItemStylePropertyChanged(Style oldValue, Style newValue) { } #endregion public Style LegendItemStyle /// /// Gets or sets the Geometry used to clip DataPoints to the PlotArea bounds. /// private RectangleGeometry ClipGeometry { get; set; } /// /// Indicates whether a call to Refresh is required when the control's /// size changes. /// private bool _needRefreshWhenSizeChanged = true; #region public TimeSpan TransitionDuration /// /// Gets or sets the duration of the value Transition animation. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register( "TransitionDuration", typeof(TimeSpan), typeof(DataPointSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #endregion public TimeSpan TransitionDuration /// /// Initializes the static members of the DataPointSeries class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static DataPointSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DataPointSeries), new FrameworkPropertyMetadata(typeof(DataPointSeries))); } /// /// Initializes a new instance of the DataPointSeries class. /// protected DataPointSeries() { ClipGeometry = new RectangleGeometry(); Clip = ClipGeometry; } /// /// Adds an object to the series host by creating a corresponding data point /// for it. /// /// The object to add to the series host. /// The data point created for the object. protected virtual DataPoint AddObject(object dataContext) { if (ShouldCreateDataPoint(dataContext)) { DataPoint dataPoint = CreateAndPrepareDataPoint(dataContext); _dataPointsByObject.Add(dataContext, dataPoint); AddDataPoint(dataPoint); return dataPoint; } return null; } /// /// Returns whether a data point should be created for the data context. /// /// The data context that will be used for the /// data point. /// A value indicating whether a data point should be created /// for the data context. protected virtual bool ShouldCreateDataPoint(object dataContext) { return true; } /// /// Returns the index at which to insert data point in the plot area /// child collection. /// /// The data point to retrieve the insertion /// index for. /// The insertion index. protected virtual int GetInsertionIndex(DataPoint dataPoint) { return PlotArea.Children.Count; } /// /// Adds a data point to the plot area. /// /// The data point to add to the plot area. /// protected virtual void AddDataPoint(DataPoint dataPoint) { if (dataPoint.IsSelected) { Select(dataPoint); } if (PlotArea != null) { // Positioning data point outside the visible area. Canvas.SetLeft(dataPoint, float.MinValue); Canvas.SetTop(dataPoint, float.MinValue); dataPoint.IsSelectionEnabled = IsSelectionEnabled; AttachEventHandlersToDataPoint(dataPoint); PlotArea.Children.Insert(GetInsertionIndex(dataPoint), dataPoint); ActiveDataPointCount++; } } /// /// Retrieves the data point corresponding to the object passed as the /// parameter. /// /// The data context used for the point. /// /// The data point associated with the object. protected virtual DataPoint GetDataPoint(object dataContext) { DataPoint dataPoint = _dataPointsByObject[dataContext].Where(dp => object.Equals(dataContext, dp.DataContext)).FirstOrDefault(); return dataPoint; } /// /// Creates and prepares a data point. /// /// The object to use as the data context /// of the data point. /// The newly created data point. private DataPoint CreateAndPrepareDataPoint(object dataContext) { DataPoint dataPoint = CreateDataPoint(); PrepareDataPoint(dataPoint, dataContext); return dataPoint; } /// /// Returns a Control suitable for the Series. /// /// The DataPoint instance. protected abstract DataPoint CreateDataPoint(); /// /// Creates a legend item. /// /// A legend item for insertion in the legend items collection. /// /// The owner of the new LegendItem. protected virtual LegendItem CreateLegendItem(DataPointSeries owner) { LegendItem legendItem = new LegendItem() { Owner = owner }; legendItem.SetBinding(LegendItem.StyleProperty, new Binding(ActualLegendItemStyleName) { Source = this }); legendItem.SetBinding(LegendItem.ContentProperty, new Binding(TitleName) { Source = this }); legendItem.SetBinding(LegendItem.VisibilityProperty, new Binding(VisibilityName) { Source = this }); return legendItem; } /// /// Method that handles the ObservableCollection.CollectionChanged event for the ItemsSource property. /// /// The object that raised the event. /// The event data. private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Pass notification on OnItemsSourceCollectionChanged(ItemsSource, e); } /// /// Updates data points collection with items retrieved from items /// source and removes the old items. /// /// The items to load. /// The items to remove. protected void LoadDataPoints(IEnumerable newItems, IEnumerable oldItems) { if ((PlotArea != null) && (SeriesHost != null)) { IList removedDataPoints = new List(); if (oldItems != null) { if (oldItems != null) { // Remove existing objects from internal collections. foreach (object dataContext in oldItems) { DataPoint removedDataPoint = RemoveObject(dataContext); _dataPointsByObject.Remove(dataContext, removedDataPoint); if (removedDataPoint != null) { removedDataPoints.Add(removedDataPoint); } } } StaggeredStateChange(removedDataPoints, removedDataPoints.Count, DataPointState.Hiding); } IList addedDataPoints = new List(); if (newItems != null) { foreach (object dataContext in newItems) { DataPoint dataPoint = AddObject(dataContext); if (dataPoint != null) { addedDataPoints.Add(dataPoint); } } } OnDataPointsChanged(addedDataPoints, removedDataPoints); } } /// /// Attaches handler plot area after loading it from XAML. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); // Get reference to new ChartArea and hook its SizeChanged event PlotArea = GetTemplateChild(PlotAreaName) as Panel; if (!TemplateApplied) { TemplateApplied = true; SizeChanged += new SizeChangedEventHandler(OnSizeChanged); } } /// /// Invokes an action when the plot area's layout is updated. /// /// The action to execute. internal void InvokeOnLayoutUpdated(Action action) { EventHandler handler = null; handler = delegate { this.PlotArea.LayoutUpdated -= handler; action(); }; this.PlotArea.LayoutUpdated += handler; } /// /// Handles changes to the SeriesHost property. /// /// Old value. /// New value. protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { base.OnSeriesHostPropertyChanged(oldValue, newValue); if (null == newValue) { // Reset flag to prepare for next addition to a series host _needRefreshWhenSizeChanged = true; } } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected virtual void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { StaggeredStateChange(newDataPoints, newDataPoints.Count(), DataPointState.Showing); } /// /// Method called when the ItemsSource collection changes. /// /// New value of the collection. /// Information about the change. protected virtual void OnItemsSourceCollectionChanged(IEnumerable collection, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Replace) { IList updatedDataPoints = new List(); for (int index = 0; index < e.OldItems.Count; index++) { DataPoint dataPointToUpdate = _dataPointsByObject[e.OldItems[index]].Where(dp => object.Equals(e.OldItems[index], dp.DataContext)).Except(updatedDataPoints).FirstOrDefault(); if (null != dataPointToUpdate) { updatedDataPoints.Add(dataPointToUpdate); dataPointToUpdate.DataContext = e.NewItems[index]; _dataPointsByObject.Remove(e.OldItems[index], dataPointToUpdate); _dataPointsByObject.Add(e.NewItems[index], dataPointToUpdate); } } } else if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Remove) { LoadDataPoints( e.NewItems, e.OldItems); } else { Refresh(); } } /// /// Removes items from the existing plot area and adds items to new /// plot area. /// /// The previous plot area. /// The new plot area. protected virtual void OnPlotAreaChanged(Panel oldValue, Panel newValue) { if (oldValue != null) { foreach (DataPoint dataPoint in ActiveDataPoints) { oldValue.Children.Remove(dataPoint); } } if (newValue != null) { foreach (DataPoint dataPoint in ActiveDataPoints) { newValue.Children.Add(dataPoint); } } } /// /// Updates the visual appearance of all the data points when the size /// changes. /// /// The source of the event. /// Information about the event. private void OnSizeChanged(object sender, SizeChangedEventArgs e) { PlotAreaSize = e.NewSize; ClipGeometry.Rect = new Rect(0, 0, PlotAreaSize.Width, PlotAreaSize.Height); if (null != PlotArea) { PlotArea.Width = PlotAreaSize.Width; PlotArea.Height = PlotAreaSize.Height; if (_needRefreshWhenSizeChanged) { _needRefreshWhenSizeChanged = false; Refresh(); } else { UpdateDataPoints(ActiveDataPoints); } } } /// /// Refreshes data from data source and renders the series. /// public void Refresh() { try { IEnumerable itemsSource = ItemsSource; LoadDataPoints(itemsSource, ActiveDataPoints.Select(dataPoint => dataPoint.DataContext)); } catch { if (DesignerProperties.GetIsInDesignMode(this)) { // Suppress exception to improve the design-time experience } else { throw; } } } /// /// Removes an object from the series host by removing its corresponding /// data point. /// /// The object to remove from the series data /// source. /// The data point corresponding to the removed object. /// protected virtual DataPoint RemoveObject(object dataContext) { DataPoint dataPoint = GetDataPoint(dataContext); if (dataPoint != null) { RemoveDataPoint(dataPoint); } return dataPoint; } /// /// Removes a data point from the plot area. /// /// The data point to remove. protected virtual void RemoveDataPoint(DataPoint dataPoint) { if (dataPoint.IsSelected) { Unselect(dataPoint); } ActiveDataPointCount--; // Cancel any Storyboards that might be holding the State property's value dataPoint.BeginAnimation(DataPoint.StateProperty, null); dataPoint.State = DataPointState.PendingRemoval; } /// /// Gets a value indicating whether all data points are being /// updated. /// protected bool UpdatingDataPoints { get; private set; } /// /// Updates the visual representation of all data points in the plot /// area. /// /// A sequence of data points to update. /// protected virtual void UpdateDataPoints(IEnumerable dataPoints) { UpdatingDataPoints = true; DetachEventHandlersFromDataPoints(dataPoints); try { OnBeforeUpdateDataPoints(); foreach (DataPoint dataPoint in dataPoints) { UpdateDataPoint(dataPoint); } OnAfterUpdateDataPoints(); } finally { AttachEventHandlersToDataPoints(dataPoints); UpdatingDataPoints = false; } } /// /// Attaches event handlers to the data points. /// /// A sequence of data points. private void AttachEventHandlersToDataPoints(IEnumerable dataPoints) { foreach (DataPoint dataPoint in dataPoints) { AttachEventHandlersToDataPoint(dataPoint); } } /// /// Detaches event handlers from the data points. /// /// A sequence of data points. private void DetachEventHandlersFromDataPoints(IEnumerable dataPoints) { foreach (DataPoint dataPoint in dataPoints) { DetachEventHandlersFromDataPoint(dataPoint); } } /// /// Attaches event handlers to a data point. /// /// The data point. protected virtual void AttachEventHandlersToDataPoint(DataPoint dataPoint) { dataPoint.IsSelectedChanged += OnDataPointIsSelectedChanged; dataPoint.ActualDependentValueChanged += OnDataPointActualDependentValueChanged; dataPoint.ActualIndependentValueChanged += OnDataPointActualIndependentValueChanged; dataPoint.DependentValueChanged += OnDataPointDependentValueChanged; dataPoint.IndependentValueChanged += OnDataPointIndependentValueChanged; dataPoint.StateChanged += OnDataPointStateChanged; } /// /// Unselects a data point. /// /// The data point to unselect. private void Unselect(DataPoint dataPoint) { if (dataPoint.DataContext.Equals(SelectedItem)) { SelectedItem = null; } } /// /// Selects a data point. /// /// The data point to select. private void Select(DataPoint dataPoint) { SelectedItem = dataPoint.DataContext; } /// /// Method executed when a data point is either selected or unselected. /// /// The source of the event. /// Information about the event. private void OnDataPointIsSelectedChanged(object sender, RoutedPropertyChangedEventArgs e) { DataPoint dataPoint = sender as DataPoint; if (e.NewValue) { Select(dataPoint); } else { Unselect(dataPoint); } } /// /// Detaches event handlers from a data point. /// /// The data point. protected virtual void DetachEventHandlersFromDataPoint(DataPoint dataPoint) { dataPoint.IsSelectedChanged -= OnDataPointIsSelectedChanged; dataPoint.ActualDependentValueChanged -= OnDataPointActualDependentValueChanged; dataPoint.ActualIndependentValueChanged -= OnDataPointActualIndependentValueChanged; dataPoint.DependentValueChanged -= OnDataPointDependentValueChanged; dataPoint.IndependentValueChanged -= OnDataPointIndependentValueChanged; dataPoint.StateChanged -= OnDataPointStateChanged; } /// /// This method that executes before data points are updated. /// protected virtual void OnBeforeUpdateDataPoints() { } /// /// This method that executes after data points are updated. /// protected virtual void OnAfterUpdateDataPoints() { } /// /// Updates the visual representation of a single data point in the plot /// area. /// /// The data point to update. protected abstract void UpdateDataPoint(DataPoint dataPoint); /// /// Prepares a data point by extracting binding it to a data context /// object. /// /// A data point. /// A data context object. protected virtual void PrepareDataPoint(DataPoint dataPoint, object dataContext) { // Create a Control with DataContext set to the data source dataPoint.DataContext = dataContext; // Set bindings for IndependentValue/DependentValue if (IndependentValueBinding != null) { dataPoint.SetBinding(DataPoint.IndependentValueProperty, IndependentValueBinding); } if (DependentValueBinding == null) { dataPoint.SetBinding(DataPoint.DependentValueProperty, new Binding()); } else { dataPoint.SetBinding(DataPoint.DependentValueProperty, DependentValueBinding); } } /// /// Reveals data points using a storyboard. /// /// The data points to change the state of. /// /// The number of data points in the sequence. /// The state to change to. private void StaggeredStateChange(IEnumerable dataPoints, int dataPointCount, DataPointState newState) { if (PlotArea == null || dataPointCount == 0) { return; } Storyboard stateChangeStoryBoard = new Storyboard(); dataPoints.ForEachWithIndex((dataPoint, count) => { // Create an Animation ObjectAnimationUsingKeyFrames objectAnimationUsingKeyFrames = new ObjectAnimationUsingKeyFrames(); Storyboard.SetTarget(objectAnimationUsingKeyFrames, dataPoint); Storyboard.SetTargetProperty(objectAnimationUsingKeyFrames, new PropertyPath("State")); // Create a key frame DiscreteObjectKeyFrame discreteObjectKeyFrame = new DiscreteObjectKeyFrame(); discreteObjectKeyFrame.Value = newState; // Create the specified animation type switch (AnimationSequence) { case AnimationSequence.Simultaneous: discreteObjectKeyFrame.KeyTime = TimeSpan.Zero; break; case AnimationSequence.FirstToLast: discreteObjectKeyFrame.KeyTime = TimeSpan.FromMilliseconds(1000 * ((double)count / dataPointCount)); break; case AnimationSequence.LastToFirst: discreteObjectKeyFrame.KeyTime = TimeSpan.FromMilliseconds(1000 * ((double)(dataPointCount - count - 1) / dataPointCount)); break; } // Add the Animation to the Storyboard objectAnimationUsingKeyFrames.KeyFrames.Add(discreteObjectKeyFrame); stateChangeStoryBoard.Children.Add(objectAnimationUsingKeyFrames); }); stateChangeStoryBoard.Duration = new Duration(AnimationSequence.Simultaneous == AnimationSequence ? TimeSpan.FromTicks(1) : TimeSpan.FromMilliseconds(1001)); _storyBoardQueue.Enqueue( stateChangeStoryBoard, (sender, args) => { stateChangeStoryBoard.Stop(); }); } /// /// Handles data point state property change. /// /// The data point. /// Information about the event. private void OnDataPointStateChanged(object sender, RoutedPropertyChangedEventArgs args) { OnDataPointStateChanged(sender as DataPoint, args.OldValue, args.NewValue); } /// /// Handles data point state property change. /// /// The data point. /// The old value. /// The new value. protected virtual void OnDataPointStateChanged(DataPoint dataPoint, DataPointState oldValue, DataPointState newValue) { if (dataPoint.State == DataPointState.Hidden) { DetachEventHandlersFromDataPoint(dataPoint); PlotArea.Children.Remove(dataPoint); } } /// /// Handles data point actual dependent value property changes. /// /// The data point. /// Information about the event. private void OnDataPointActualDependentValueChanged(object sender, RoutedPropertyChangedEventArgs args) { OnDataPointActualDependentValueChanged(sender as DataPoint, args.OldValue, args.NewValue); } /// /// Handles data point actual dependent value property change. /// /// The data point. /// The old value. /// The new value. protected virtual void OnDataPointActualDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue) { } /// /// Handles data point actual independent value property changes. /// /// The data point. /// Information about the event. private void OnDataPointActualIndependentValueChanged(object sender, RoutedPropertyChangedEventArgs args) { OnDataPointActualIndependentValueChanged(sender as DataPoint, args.OldValue, args.NewValue); } /// /// Handles data point actual independent value property change. /// /// The data point. /// The old value. /// The new value. protected virtual void OnDataPointActualIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { } /// /// Handles data point dependent value property changes. /// /// The data point. /// Information about the event. private void OnDataPointDependentValueChanged(object sender, RoutedPropertyChangedEventArgs args) { OnDataPointDependentValueChanged(sender as DataPoint, args.OldValue, args.NewValue); } /// /// Handles data point dependent value property change. /// /// The data point. /// The old value. /// The new value. protected virtual void OnDataPointDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue) { } /// /// Handles data point independent value property changes. /// /// The data point. /// Information about the event. private void OnDataPointIndependentValueChanged(object sender, RoutedPropertyChangedEventArgs args) { OnDataPointIndependentValueChanged(sender as DataPoint, args.OldValue, args.NewValue); } /// /// Handles data point independent value property change. /// /// The data point. /// The old value. /// The new value. protected virtual void OnDataPointIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { } /// /// Returns a ResourceDictionaryEnumerator that returns ResourceDictionaries with a /// DataPointStyle having the specified TargetType or with a TargetType that is an /// ancestor of the specified type. /// /// The ResourceDictionaryDispenser. /// The TargetType. /// A value indicating whether to accept ancestors of the TargetType. /// A ResourceDictionary enumerator. internal static IEnumerator GetResourceDictionaryWithTargetType(IResourceDictionaryDispenser dispenser, Type targetType, bool takeAncestors) { return dispenser.GetResourceDictionariesWhere(dictionary => { Style style = dictionary[DataPointStyleName] as Style; if (null != style) { return (null != style.TargetType) && ((targetType == style.TargetType) || (takeAncestors && style.TargetType.IsAssignableFrom(targetType))); } return false; }); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/DataPointSeriesWithAxes.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.DataVisualization; using System.Windows.Controls.DataVisualization.Collections; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a dynamic series that uses axes to display data points. /// /// Preview public abstract class DataPointSeriesWithAxes : DataPointSeries, IDataProvider, IRangeProvider, IAxisListener, IValueMarginProvider { /// /// Gets or sets the data points by dependent value. /// private OrderedMultipleDictionary DataPointsByActualDependentValue { get; set; } /// /// Creates the correct range axis based on the data. /// /// The value to evaluate to determine which type of /// axis to create. /// The range axis appropriate that can plot the provided /// value. protected static IRangeAxis CreateRangeAxisFromData(object value) { double doubleValue; DateTime dateTime; if (ValueHelper.TryConvert(value, out doubleValue)) { return new LinearAxis(); } else if (ValueHelper.TryConvert(value, out dateTime)) { return new DateTimeAxis(); } else { return null; } } /// /// Retrieves the value for a given access from a data point. /// /// The data point to retrieve the value from. /// The axis to retrieve the value for. /// A function that returns a value appropriate for the axis /// when provided a DataPoint. protected virtual object GetActualDataPointAxisValue(DataPoint dataPoint, IAxis axis) { if (axis == InternalActualIndependentAxis) { return dataPoint.ActualIndependentValue; } else if (axis == InternalActualDependentAxis) { return dataPoint.ActualDependentValue; } return null; } /// /// Gets or sets the actual dependent axis. /// protected IAxis InternalActualDependentAxis { get; set; } #region public Axis InternalDependentAxis /// /// Stores the internal dependent axis. /// private IAxis _internalDependentAxis; /// /// Gets or sets the value of the internal dependent axis. /// protected IAxis InternalDependentAxis { get { return _internalDependentAxis; } set { if (_internalDependentAxis != value) { IAxis oldValue = _internalDependentAxis; _internalDependentAxis = value; OnInternalDependentAxisPropertyChanged(oldValue, value); } } } /// /// DependentAxisProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnInternalDependentAxisPropertyChanged(IAxis oldValue, IAxis newValue) { if (newValue != null && InternalActualDependentAxis != null && InternalActualDependentAxis != newValue && InternalActualDependentAxis.RegisteredListeners.Contains(this)) { InternalActualDependentAxis.RegisteredListeners.Remove(this); InternalActualDependentAxis = null; GetAxes(); } } #endregion public Axis InternalDependentAxis /// /// Gets or sets the actual independent axis value. /// protected IAxis InternalActualIndependentAxis { get; set; } #region protected Axis InternalIndependentAxis /// /// The internal independent axis. /// private IAxis _internalIndependentAxis; /// /// Gets or sets the value of the internal independent axis. /// protected IAxis InternalIndependentAxis { get { return _internalIndependentAxis; } set { if (value != _internalIndependentAxis) { IAxis oldValue = _internalIndependentAxis; _internalIndependentAxis = value; OnInternalIndependentAxisPropertyChanged(oldValue, value); } } } /// /// IndependentAxisProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnInternalIndependentAxisPropertyChanged(IAxis oldValue, IAxis newValue) { if (newValue != null && InternalActualIndependentAxis != null && InternalActualIndependentAxis != newValue && InternalActualIndependentAxis.RegisteredListeners.Contains(this)) { InternalActualIndependentAxis.RegisteredListeners.Remove(this); InternalActualIndependentAxis = null; GetAxes(); } } #endregion protected Axis IndependentAxis /// /// Initializes a new instance of the DataPointSeriesWithAxes class. /// protected DataPointSeriesWithAxes() { this.DataPointsByActualDependentValue = new OrderedMultipleDictionary( false, (left, right) => left.CompareTo(right), (leftDataPoint, rightDataPoint) => RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint))); } /// /// Update the axes when the specified data point's ActualDependentValue property changes. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointActualDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue) { if (oldValue != null) { bool removed = DataPointsByActualDependentValue.Remove(oldValue, dataPoint); if (removed) { DataPointsByActualDependentValue.Add(newValue, dataPoint); } } UpdateActualDependentAxis(); UpdateDataPoint(dataPoint); base.OnDataPointActualDependentValueChanged(dataPoint, oldValue, newValue); } /// /// Update the axes when the specified data point's DependentValue property changes. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue) { if ((null != InternalActualDependentAxis)) { dataPoint.BeginAnimation(DataPoint.ActualDependentValueProperty, "ActualDependentValue", newValue, this.TransitionDuration, this.TransitionEasingFunction); } else { dataPoint.ActualDependentValue = newValue; } base.OnDataPointDependentValueChanged(dataPoint, oldValue, newValue); } /// /// Update axes when the specified data point's effective dependent value changes. /// private void UpdateActualDependentAxis() { if (InternalActualDependentAxis != null) { IDataConsumer dataConsumer = InternalActualDependentAxis as IDataConsumer; if (dataConsumer != null) { IDataProvider categoryInformationProvider = (IDataProvider)this; dataConsumer.DataChanged(categoryInformationProvider, categoryInformationProvider.GetData(dataConsumer)); } IRangeConsumer rangeAxis = InternalActualDependentAxis as IRangeConsumer; if (rangeAxis != null) { IRangeProvider rangeInformationProvider = (IRangeProvider)this; rangeAxis.RangeChanged(rangeInformationProvider, rangeInformationProvider.GetRange(rangeAxis)); } } } /// /// Update axes when the specified data point's actual independent value changes. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointActualIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { UpdateActualIndependentAxis(); UpdateDataPoint(dataPoint); base.OnDataPointActualIndependentValueChanged(dataPoint, oldValue, newValue); } /// /// Update axes when the specified data point's independent value changes. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { if ((null != InternalActualIndependentAxis) && (InternalActualIndependentAxis is IRangeAxis)) { dataPoint.BeginAnimation(DataPoint.ActualIndependentValueProperty, "ActualIndependentValue", newValue, this.TransitionDuration, this.TransitionEasingFunction); } else { dataPoint.ActualIndependentValue = newValue; } base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue); } /// /// Update axes when a data point's effective independent value changes. /// private void UpdateActualIndependentAxis() { if (InternalActualIndependentAxis != null) { ICategoryAxis categoryAxis = InternalActualIndependentAxis as ICategoryAxis; if (categoryAxis != null) { IDataProvider categoryInformationProvider = (IDataProvider)this; categoryAxis.DataChanged(categoryInformationProvider, categoryInformationProvider.GetData(categoryAxis)); } IRangeConsumer rangeAxis = InternalActualIndependentAxis as IRangeConsumer; if (rangeAxis != null) { IRangeProvider rangeInformationProvider = (IRangeProvider)this; rangeAxis.RangeChanged(rangeInformationProvider, rangeInformationProvider.GetRange(rangeAxis)); } } } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected override void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { foreach (DataPoint dataPoint in newDataPoints) { DataPointsByActualDependentValue.Add(dataPoint.ActualDependentValue, dataPoint); } foreach (DataPoint dataPoint in oldDataPoints) { DataPointsByActualDependentValue.Remove(dataPoint.ActualDependentValue, dataPoint); } GetAxes(); if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null) { Action action = () => { AxesInvalidated = false; UpdatingAllAxes = true; try { UpdateActualIndependentAxis(); UpdateActualDependentAxis(); } finally { UpdatingAllAxes = false; } if (AxesInvalidated) { UpdateDataPoints(ActiveDataPoints); } else { UpdateDataPoints(newDataPoints); } AxesInvalidated = false; }; InvokeOnLayoutUpdated(action); } base.OnDataPointsChanged(newDataPoints, oldDataPoints); } /// /// Gets or sets a value indicating whether to the axes are being /// updated. /// private bool UpdatingAllAxes { get; set; } /// /// Gets or sets a value indicating whether the axes have been /// invalidated. /// private bool AxesInvalidated { get; set; } /// /// Only updates all data points if series has axes. /// /// A sequence of data points to update. /// protected override void UpdateDataPoints(IEnumerable dataPoints) { if (InternalActualIndependentAxis != null && InternalActualDependentAxis != null) { base.UpdateDataPoints(dataPoints); } } /// /// Method called to get series to acquire the axes it needs. Acquires /// no axes by default. /// private void GetAxes() { if (SeriesHost != null) { DataPoint firstDataPoint = ActiveDataPoints.FirstOrDefault(); if (firstDataPoint == null) { return; } GetAxes(firstDataPoint); } } /// /// Method called to get series to acquire the axes it needs. Acquires /// no axes by default. /// /// The first data point. protected abstract void GetAxes(DataPoint firstDataPoint); /// /// Method called to get the axes that the series needs. /// /// The first data point. /// A predicate that returns /// a value indicating whether an axis is an acceptable candidate for /// the series independent axis. /// A function that creates an /// acceptable independent axis. /// A predicate that returns /// a value indicating whether an axis is an acceptable candidate for /// the series dependent axis. /// A function that creates an /// acceptable dependent axis. protected virtual void GetAxes(DataPoint firstDataPoint, Func independentAxisPredicate, Func independentAxisFactory, Func dependentAxisPredicate, Func dependentAxisFactory) { Func actualIndependentAxisPredicate = (axis) => independentAxisPredicate(axis) && axis.CanPlot(firstDataPoint.IndependentValue); IAxis workingIndependentAxis = null; if (this.InternalActualIndependentAxis == null) { if (this.InternalIndependentAxis != null) { if (actualIndependentAxisPredicate(this.InternalIndependentAxis)) { workingIndependentAxis = this.InternalIndependentAxis; } else { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_GetAxes_AssignedIndependentAxisCannotBeUsed); } } if (workingIndependentAxis == null) { workingIndependentAxis = this.SeriesHost.Axes.FirstOrDefault(actualIndependentAxisPredicate); } if (workingIndependentAxis == null) { workingIndependentAxis = independentAxisFactory(); } this.InternalActualIndependentAxis = workingIndependentAxis; if (!workingIndependentAxis.RegisteredListeners.Contains(this)) { workingIndependentAxis.RegisteredListeners.Add(this); } if (!this.SeriesHost.Axes.Contains(workingIndependentAxis)) { this.SeriesHost.Axes.Add(workingIndependentAxis); } } Func actualDependentAxisPredicate = (axis) => dependentAxisPredicate(axis) && axis.CanPlot(firstDataPoint.DependentValue); IAxis workingDependentAxis = null; if (this.InternalActualDependentAxis == null) { if (this.InternalDependentAxis != null) { if (actualDependentAxisPredicate(this.InternalDependentAxis)) { workingDependentAxis = this.InternalDependentAxis; } else { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_GetAxes_AssignedDependentAxisCannotBeUsed); } } if (workingDependentAxis == null) { workingDependentAxis = InternalActualIndependentAxis.DependentAxes.Concat(this.SeriesHost.Axes).FirstOrDefault(actualDependentAxisPredicate); } if (workingDependentAxis == null) { workingDependentAxis = dependentAxisFactory(); } this.InternalActualDependentAxis = workingDependentAxis; if (!workingDependentAxis.RegisteredListeners.Contains(this)) { workingDependentAxis.RegisteredListeners.Add(this); } // Only add axis to the axes collection of the series host if // it is not a dependent axis belonging to the acquired // independent axis. if (!this.SeriesHost.Axes.Contains(workingDependentAxis) && !InternalActualIndependentAxis.DependentAxes.Contains(workingDependentAxis)) { this.SeriesHost.Axes.Add(workingDependentAxis); } } } /// /// Updates data points when the axis is invalidated. /// /// The axis that was invalidated. void IAxisListener.AxisInvalidated(IAxis axis) { if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null && PlotArea != null) { if (!UpdatingAllAxes) { UpdateDataPoints(ActiveDataPoints); } else { AxesInvalidated = true; } } } /// /// Returns the actual range of data for a given axis. /// /// The axis to retrieve the range for. /// The actual range of data. protected virtual Range GetRange(IRangeConsumer consumer) { if (consumer == null) { throw new ArgumentNullException("consumer"); } if (consumer == InternalActualDependentAxis) { if (this.DataPointsByActualDependentValue.Count > 0) { return this.DataPointsByActualDependentValue.GetKeyRange(); } } IAxis axis = consumer as IAxis; return (axis != null) ? ActiveDataPoints.Select(dataPoint => (IComparable)GetActualDataPointAxisValue(dataPoint, axis)).GetRange() : new Range(); } /// /// Returns the value margins for a given axis. /// /// The axis to retrieve the value margins for. /// /// A sequence of value margins. protected virtual IEnumerable GetValueMargins(IValueMarginConsumer consumer) { IAxis axis = consumer as IAxis; if (axis != null && ActiveDataPoints.Any()) { Func selector = null; DataPoint minimumPoint = null; DataPoint maximumPoint = null; double margin = 0.0; if (axis == InternalActualIndependentAxis) { selector = (dataPoint) => (IComparable)dataPoint.ActualIndependentValue; minimumPoint = ActiveDataPoints.MinOrNull(selector); maximumPoint = ActiveDataPoints.MaxOrNull(selector); margin = minimumPoint.GetActualMargin(this.InternalActualIndependentAxis); } else if (axis == InternalActualDependentAxis) { selector = (dataPoint) => (IComparable)dataPoint.ActualDependentValue; Tuple largestAndSmallestValues = this.DataPointsByActualDependentValue.GetLargestAndSmallestValues(); minimumPoint = largestAndSmallestValues.Item1; maximumPoint = largestAndSmallestValues.Item2; margin = minimumPoint.GetActualMargin(this.InternalActualDependentAxis); } yield return new ValueMargin(selector(minimumPoint), margin, margin); yield return new ValueMargin(selector(maximumPoint), margin, margin); } else { yield break; } } /// /// Returns data to a data consumer. /// /// The data consumer requesting the data. /// /// The data for a given data consumer. IEnumerable IDataProvider.GetData(IDataConsumer dataConsumer) { IAxis axis = (IAxis)dataConsumer; if (axis == null) { throw new ArgumentNullException("dataConsumer"); } Func selector = null; if (axis == InternalActualIndependentAxis) { if (IndependentValueBinding == null) { return Enumerable.Range(1, ActiveDataPointCount).CastWrapper(); } selector = (dataPoint) => dataPoint.ActualIndependentValue ?? dataPoint.ActualDependentValue; } else if (axis == InternalActualDependentAxis) { selector = (dataPoint) => dataPoint.ActualDependentValue; } return ActiveDataPoints.Select(selector).Distinct(); } /// /// Called when the value of the SeriesHost property changes. /// /// The value to be replaced. /// The new series host value. protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { if (oldValue != null) { if (InternalActualIndependentAxis != null) { InternalActualIndependentAxis.RegisteredListeners.Remove(this); InternalActualIndependentAxis = null; } if (InternalActualDependentAxis != null) { InternalActualDependentAxis.RegisteredListeners.Remove(this); InternalActualDependentAxis = null; } } base.OnSeriesHostPropertyChanged(oldValue, newValue); } /// /// Returns the data range. /// /// The consumer requesting the range. /// The data range. Range IRangeProvider.GetRange(IRangeConsumer rangeConsumer) { return GetRange(rangeConsumer); } /// /// Returns the value margins for a given axis. /// /// The axis to retrieve the value margins for. /// /// A sequence of value margins. IEnumerable IValueMarginProvider.GetValueMargins(IValueMarginConsumer axis) { return GetValueMargins(axis); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/DataPointSingleSeriesWithAxes.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { /// /// A dynamic series with axes and only one legend item and style for all /// data points. /// /// Preview public abstract class DataPointSingleSeriesWithAxes : DataPointSeriesWithAxes, IRequireGlobalSeriesIndex { /// /// Name of the ActualDataPointStyle property. /// protected const string ActualDataPointStyleName = "ActualDataPointStyle"; /// /// Gets the single legend item associated with the series. /// protected LegendItem LegendItem { get { if (null == _legendItem) { _legendItem = CreateLegendItem(this); LegendItems.Add(_legendItem); } return _legendItem; } } /// /// Stores the LegendItem for the series. /// private LegendItem _legendItem; /// /// Gets the Palette-dispensed ResourceDictionary for the Series. /// protected ResourceDictionary PaletteResources { get; private set; } /// /// Gets or sets a value indicating whether a custom title is in use. /// private bool CustomTitleInUse { get; set; } /// /// DataPointStyleProperty property changed handler. /// /// Old value. /// New value. protected override void OnDataPointStylePropertyChanged(Style oldValue, Style newValue) { // Propagate change ActualDataPointStyle = newValue; base.OnDataPointStylePropertyChanged(oldValue, newValue); } #region protected Style ActualDataPointStyle /// /// Gets or sets the actual style used for the data points. /// protected Style ActualDataPointStyle { get { return GetValue(ActualDataPointStyleProperty) as Style; } set { SetValue(ActualDataPointStyleProperty, value); } } /// /// Identifies the ActualDataPointStyle dependency property. /// protected static readonly DependencyProperty ActualDataPointStyleProperty = DependencyProperty.Register( ActualDataPointStyleName, typeof(Style), typeof(DataPointSingleSeriesWithAxes), null); #endregion protected Style ActualDataPointStyle #region protected Style ActualLegendItemStyle /// /// Gets or sets the actual style used for the legend item. /// protected Style ActualLegendItemStyle { get { return GetValue(ActualLegendItemStyleProperty) as Style; } set { SetValue(ActualLegendItemStyleProperty, value); } } /// /// Identifies the ActualLegendItemStyle dependency property. /// protected static readonly DependencyProperty ActualLegendItemStyleProperty = DependencyProperty.Register( ActualLegendItemStyleName, typeof(Style), typeof(DataPointSeries), null); #endregion protected Style ActualLegendItemStyle /// /// Called when the value of the LegendItemStyle property changes. /// /// Old value. /// New value. protected override void OnLegendItemStylePropertyChanged(Style oldValue, Style newValue) { // Propagate change ActualLegendItemStyle = newValue; base.OnLegendItemStylePropertyChanged(oldValue, newValue); } #region public int? GlobalSeriesIndex /// /// Gets the index of the series in the Parent's series collection. /// public int? GlobalSeriesIndex { get { return (int?)GetValue(GlobalSeriesIndexProperty); } private set { SetValue(GlobalSeriesIndexProperty, value); } } /// /// Identifies the GlobalSeriesIndex dependency property. /// public static readonly DependencyProperty GlobalSeriesIndexProperty = DependencyProperty.Register( "GlobalSeriesIndex", typeof(int?), typeof(Series), new PropertyMetadata(new int?(), OnGlobalSeriesIndexPropertyChanged)); /// /// GlobalSeriesIndexProperty property changed handler. /// /// Series that changed its Index. /// Event arguments. private static void OnGlobalSeriesIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataPointSingleSeriesWithAxes source = (DataPointSingleSeriesWithAxes)d; int? oldValue = (int?)e.OldValue; int? newValue = (int?)e.NewValue; source.OnGlobalSeriesIndexPropertyChanged(oldValue, newValue); } /// /// GlobalSeriesIndexProperty property changed handler. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "newValue+1", Justification = "Impractical to add as many Series as it would take to overflow.")] protected virtual void OnGlobalSeriesIndexPropertyChanged(int? oldValue, int? newValue) { if (!CustomTitleInUse && (null == GetBindingExpression(TitleProperty))) { Title = newValue.HasValue ? string.Format(CultureInfo.CurrentCulture, Properties.Resources.Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString, newValue.Value + 1) : null; // Setting Title will set CustomTitleInUse; reset it now CustomTitleInUse = false; } } #endregion public int? GlobalSeriesIndex /// /// Called when the Title property changes. /// /// Old value of the Title property. /// New value of the Title property. protected override void OnTitleChanged(object oldValue, object newValue) { // Title property is being set, so a custom Title is in use CustomTitleInUse = true; } /// /// Initializes a new instance of the DataPointSingleSeriesWithAxes class. /// protected DataPointSingleSeriesWithAxes() { } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This property does more work than get functions typically do.")] protected abstract IEnumerator GetResourceDictionaryEnumeratorFromHost(); /// /// Insert grid containing data point used for legend item into the /// plot area. /// /// The old plot area. /// The new plot area. protected override void OnPlotAreaChanged(Panel oldValue, Panel newValue) { if (newValue != null) { CreateLegendItemDataPoint(); } base.OnPlotAreaChanged(oldValue, newValue); } /// /// When the series host property is set retrieves a style to use for all the /// data points. /// /// The old series host value. /// The new series host value. protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { base.OnSeriesHostPropertyChanged(oldValue, newValue); if (oldValue != null) { oldValue.ResourceDictionariesChanged -= new EventHandler(SeriesHostResourceDictionariesChanged); } if (newValue != null) { newValue.ResourceDictionariesChanged += new EventHandler(SeriesHostResourceDictionariesChanged); } DispensedResourcesChanging(); } /// /// Creates the LegendItem Control if conditions are right. /// private void CreateLegendItemDataPoint() { DataPoint dataPoint = CreateDataPoint(); if (null != PlotArea) { // Bounce into the visual tree to get default Style applied PlotArea.Children.Add(dataPoint); PlotArea.Children.Remove(dataPoint); } dataPoint.SetBinding(DataPoint.StyleProperty, new Binding(ActualDataPointStyleName) { Source = this }); // Start DataContext null to avoid Binding warnings in the output window LegendItem.DataContext = null; if (null == LegendItem.Parent) { LegendItem.Loaded += delegate { // Wait for Loaded to set the DataPoint LegendItem.DataContext = dataPoint; }; } else { LegendItem.DataContext = dataPoint; } } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected override void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { base.OnDataPointsChanged(newDataPoints, oldDataPoints); if (null != PlotArea) { // Create the Control for use by LegendItem // Add it to the visual tree so that its style will be applied if (null != LegendItem.DataContext) { PlotArea.Children.Remove(LegendItem.DataContext as UIElement); } } } /// /// Sets the style of the data point to the single style used for all /// data points. /// /// The data point to apply the style to. /// /// The object associated with the data point. /// protected override void PrepareDataPoint(DataPoint dataPoint, object dataContext) { dataPoint.SetBinding(DataPoint.StyleProperty, new Binding(ActualDataPointStyleName) { Source = this }); base.PrepareDataPoint(dataPoint, dataContext); } /// /// This method updates the global series index property. /// /// The global index of the series. public void GlobalSeriesIndexChanged(int? globalIndex) { this.GlobalSeriesIndex = globalIndex; } /// /// Handles the SeriesHost's ResourceDictionariesChanged event. /// /// ISeriesHost instance. /// Event args. private void SeriesHostResourceDictionariesChanged(object sender, EventArgs e) { DispensedResourcesChanging(); } /// /// Processes the change of the DispensedResources property. /// private void DispensedResourcesChanging() { if (null != PaletteResources) { Resources.MergedDictionaries.Remove(PaletteResources); PaletteResources = null; } if (SeriesHost != null) { using (IEnumerator enumerator = GetResourceDictionaryEnumeratorFromHost()) { if (enumerator.MoveNext()) { PaletteResources = enumerator.Current; Resources.MergedDictionaries.Add(PaletteResources); } } } CreateLegendItemDataPoint(); ActualDataPointStyle = DataPointStyle ?? (Resources[DataPointStyleName] as Style); ActualLegendItemStyle = LegendItemStyle ?? (Resources[LegendItemStyleName] as Style); Refresh(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/DefinitionSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Windows.Controls.DataVisualization.Charting.Primitives; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Markup; using System.Windows.Media; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Implements a series that is defined by one or more instances of the DefinitionSeries class. /// /// Preview [ContentProperty("SeriesDefinitions")] [TemplatePart(Name = SeriesAreaName, Type = typeof(Grid))] [TemplatePart(Name = ItemContainerName, Type = typeof(DelegatingListBox))] [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Class is maintainable.")] public abstract class DefinitionSeries : Control, ISeries, IAxisListener, IRangeProvider, IValueMarginProvider, IDataProvider, ISeriesHost { /// /// Name of the SeriesArea property. /// private const string SeriesAreaName = "SeriesArea"; /// /// Name of the ItemContainer property. /// private const string ItemContainerName = "ItemContainer"; /// /// Gets or sets a value indicating whether the series is 100% stacked (versus normally stacked). /// protected bool IsStacked100 { get; set; } /// /// Gets the collection of DataItems representing the data of the series. /// protected ObservableCollection DataItems { get; private set; } /// /// Gets the SeriesArea template part instance. /// protected Panel SeriesArea { get; private set; } /// /// Stores an aggregated collection of legend items from the series definitions. /// private readonly AggregatedObservableCollection _legendItems = new AggregatedObservableCollection(); /// /// Stores the collection of SeriesDefinitions that define the series. /// private readonly ObservableCollection _seriesDefinitions = new UniqueObservableCollection(); /// /// Stores a mirror collection of ISeries corresponding directly to the collection of SeriesDefinitions. /// /// /// Not using ObservableCollectionListAdapter because of race condition on ItemsChanged event /// private readonly ObservableCollection _seriesDefinitionsAsISeries = new ObservableCollection(); /// /// Keeps the SeriesDefinitions collection synchronized with the Children collection of the SeriesArea. /// private readonly ObservableCollectionListAdapter _seriesAreaChildrenListAdapter = new ObservableCollectionListAdapter(); /// /// Stores the clip geometry for the ItemContainer. /// private readonly RectangleGeometry _clipGeometry = new RectangleGeometry(); /// /// Stores a reference to the ItemContainer template part. /// private DelegatingListBox _itemContainer; /// /// Tracks the collection of DataItem that are queued for update. /// private readonly List _queueUpdateDataItemPlacement_DataItems = new List(); /// /// Tracks whether the dependent axis values changed for the next update. /// private bool _queueUpdateDataItemPlacement_DependentAxisValuesChanged; /// /// Tracks whether the independent axis values changed for the next update. /// private bool _queueUpdateDataItemPlacement_IndependentAxisValuesChanged; /// /// Stores a reference to the backing collection for the SelectedItems property. /// private ObservableCollection _selectedItems = new ObservableCollection(); /// /// Tracks whether the SelectedItems collection is being synchronized (to prevent reentrancy). /// private bool _synchronizingSelectedItems; /// /// Performs one-time initialization of DefinitionSeries data. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static DefinitionSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DefinitionSeries), new FrameworkPropertyMetadata(typeof(DefinitionSeries))); } /// /// Initializes a new instance of the DefinitionSeries class. /// protected DefinitionSeries() { _seriesDefinitions.CollectionChanged += new NotifyCollectionChangedEventHandler(SeriesDefinitionsCollectionChanged); _seriesAreaChildrenListAdapter.Collection = _seriesDefinitions; _selectedItems.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedItemsCollectionChanged); DataItems = new ObservableCollection(); } /// /// Gets or sets the dependent axis of the series. /// public IAxis DependentAxis { get { return (IAxis)GetValue(DependentAxisProperty); } set { SetValue(DependentAxisProperty, value); } } /// /// Identifies the DependentAxis dependency property. /// public static readonly DependencyProperty DependentAxisProperty = DependencyProperty.Register("DependentAxis", typeof(IAxis), typeof(DefinitionSeries), new PropertyMetadata(OnDependentAxisChanged)); /// /// Handles changes to the DependentAxis dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnDependentAxisChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((DefinitionSeries)o).OnDependentAxisChanged((IAxis)e.OldValue, (IAxis)e.NewValue); } /// /// Handles changes to the DependentAxis property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnDependentAxisChanged(IAxis oldValue, IAxis newValue) { if (null != ActualDependentAxis) { EnsureAxes(true, false, false); } } /// /// Gets or sets the independent axis of the series. /// public IAxis IndependentAxis { get { return (IAxis)GetValue(IndependentAxisProperty); } set { SetValue(IndependentAxisProperty, value); } } /// /// Identifies the IndependentAxis dependency property. /// public static readonly DependencyProperty IndependentAxisProperty = DependencyProperty.Register("IndependentAxis", typeof(IAxis), typeof(DefinitionSeries), new PropertyMetadata(OnIndependentAxisChanged)); /// /// Handles changes to the IndependentAxis dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnIndependentAxisChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((DefinitionSeries)o).OnIndependentAxisChanged((IAxis)e.OldValue, (IAxis)e.NewValue); } /// /// Handles changes to the IndependentAxis property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnIndependentAxisChanged(IAxis oldValue, IAxis newValue) { if (null != ActualIndependentAxis) { EnsureAxes(false, true, false); } } /// /// Gets the rendered dependent axis of the series. /// public IAxis ActualDependentAxis { get { return (IAxis)GetValue(ActualDependentAxisProperty); } protected set { SetValue(ActualDependentAxisProperty, value); } } /// /// Identifies the ActualDependentAxis dependency property. /// public static readonly DependencyProperty ActualDependentAxisProperty = DependencyProperty.Register("ActualDependentAxis", typeof(IAxis), typeof(DefinitionSeries), null); /// /// Gets the rendered independent axis of the series. /// public IAxis ActualIndependentAxis { get { return (IAxis)GetValue(ActualIndependentAxisProperty); } protected set { SetValue(ActualIndependentAxisProperty, value); } } /// /// Identifies the ActualIndependentAxis dependency property. /// public static readonly DependencyProperty ActualIndependentAxisProperty = DependencyProperty.Register("ActualIndependentAxis", typeof(IAxis), typeof(DefinitionSeries), null); /// /// Gets the ActualDependentAxis as an IRangeAxis instance. /// protected IRangeAxis ActualDependentRangeAxis { get { return (IRangeAxis)ActualDependentAxis; } } /// /// Gets the collection of legend items for the series. /// public ObservableCollection LegendItems { get { return _legendItems; } } /// /// Gets or sets the SeriesHost for the series. /// public ISeriesHost SeriesHost { get { return _seriesHost; } set { if (null != _seriesHost) { _seriesHost.ResourceDictionariesChanged -= new EventHandler(SeriesHostResourceDictionariesChanged); if (null != ActualDependentAxis) { ActualDependentAxis.RegisteredListeners.Remove(this); ActualDependentAxis = null; } if (null != ActualIndependentAxis) { ActualIndependentAxis.RegisteredListeners.Remove(this); ActualIndependentAxis = null; } foreach (SeriesDefinition definition in SeriesDefinitions) { SeriesDefinitionItemsSourceChanged(definition, definition.ItemsSource, null); } } _seriesHost = value; SeriesHostResourceDictionariesChanged(null, null); if (null != _seriesHost) { _seriesHost.ResourceDictionariesChanged += new EventHandler(SeriesHostResourceDictionariesChanged); foreach (SeriesDefinition definition in SeriesDefinitions) { SeriesDefinitionItemsSourceChanged(definition, null, definition.ItemsSource); } } } } /// /// Stores the SeriesHost for the series. /// private ISeriesHost _seriesHost; /// /// Gets or sets the collection of SeriesDefinitions that define the series. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] public Collection SeriesDefinitions { get { return _seriesDefinitions; } set { throw new NotSupportedException(Properties.Resources.DefinitionSeries_SeriesDefinitions_SetterNotSupported); } } /// /// Gets or sets the SelectionMode property. /// public SeriesSelectionMode SelectionMode { get { return (SeriesSelectionMode)GetValue(SelectionModeProperty); } set { SetValue(SelectionModeProperty, value); } } /// /// Identifies the SelectionMode dependency property. /// public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register("SelectionMode", typeof(SeriesSelectionMode), typeof(DefinitionSeries), new PropertyMetadata(SeriesSelectionMode.None, OnSelectionModeChanged)); /// /// Handles changes to the SelectionMode dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnSelectionModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((DefinitionSeries)o).OnSelectionModeChanged((SeriesSelectionMode)e.OldValue, (SeriesSelectionMode)e.NewValue); } /// /// Handles changes to the SelectionMode property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnSelectionModeChanged(SeriesSelectionMode oldValue, SeriesSelectionMode newValue) { if (null != _itemContainer) { switch (newValue) { case SeriesSelectionMode.None: _itemContainer.SelectedItem = null; _itemContainer.SelectionMode = Controls.SelectionMode.Single; break; case SeriesSelectionMode.Single: _itemContainer.SelectionMode = Controls.SelectionMode.Single; break; case SeriesSelectionMode.Multiple: _itemContainer.SelectionMode = Controls.SelectionMode.Multiple; break; } } } /// /// Gets or sets the SelectedIndex property. /// public int SelectedIndex { get { return (int)GetValue(SelectedIndexProperty); } set { SetValue(SelectedIndexProperty, value); } } /// /// Identifies the SelectedIndex dependency property. /// public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof(int), typeof(DefinitionSeries), new PropertyMetadata(-1)); /// /// Gets or sets the SelectedItem property. /// public object SelectedItem { get { return (object)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } /// /// Identifies the SelectedItem dependency property. /// public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(DefinitionSeries), null); /// /// Gets the currently selected items. /// /// /// This property is meant to be used when SelectionMode is Multiple. If the selection mode is Single the correct property to use is SelectedItem. /// public IList SelectedItems { get { return _selectedItems; } } /// /// Handles the CollectionChanged event of the SelectedItems collection. /// /// Event source. /// Event arguments. private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (!_synchronizingSelectedItems) { try { _synchronizingSelectedItems = true; // Synchronize the SelectedItems collection if (null != _itemContainer) { if (NotifyCollectionChangedAction.Reset == e.Action) { if (0 < _itemContainer.SelectedItems.Count) { _itemContainer.SelectedItems.Clear(); } foreach (DataItem dataItem in _selectedItems.SelectMany(v => DataItems.Where(di => object.Equals(di.Value, v)))) { _itemContainer.SelectedItems.Add(dataItem); } } else { if (null != e.OldItems) { foreach (DataItem dataItem in e.OldItems.CastWrapper().SelectMany(v => DataItems.Where(di => object.Equals(di.Value, v)))) { _itemContainer.SelectedItems.Remove(dataItem); } } if (null != e.NewItems) { foreach (DataItem dataItem in e.NewItems.CastWrapper().SelectMany(v => DataItems.Where(di => object.Equals(di.Value, v)))) { _itemContainer.SelectedItems.Add(dataItem); } } } } } finally { _synchronizingSelectedItems = false; } } } /// /// Handles the SelectionChanged event of the ItemContainer class. /// /// Event source. /// Event arguments. private void ItemContainerSelectionChanged(object sender, SelectionChangedEventArgs e) { DataItem[] removedDataItems = e.RemovedItems.CastWrapper().ToArray(); DataItem[] addedDataItems = e.AddedItems.CastWrapper().ToArray(); if (!_synchronizingSelectedItems) { try { _synchronizingSelectedItems = true; // Synchronize the SelectedItems collection foreach (object obj in removedDataItems.Select(di => di.Value)) { _selectedItems.Remove(obj); } foreach (object obj in addedDataItems.Select(di => di.Value)) { _selectedItems.Add(obj); } } finally { _synchronizingSelectedItems = false; } } // Pass the SelectionChanged event on to any listeners IList removedItems = removedDataItems.Select(di => di.Value).ToArray(); IList addedItems = addedDataItems.Select(di => di.Value).ToArray(); RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, removedItems, addedItems)); } /// /// Occurs when the selection of a DefinitionSeries changes. /// public event SelectionChangedEventHandler SelectionChanged { add { AddHandler(SelectionChangedEvent, value); } remove { RemoveHandler(SelectionChangedEvent, value); } } /// /// Identifies the SelectionChanged routed event. /// public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(DefinitionSeries)); /// /// Builds the visual tree for the control when a new template is applied. /// public override void OnApplyTemplate() { if (null != _itemContainer) { _itemContainer.PrepareContainerForItem = null; _itemContainer.ClearContainerForItem = null; _itemContainer.ItemsSource = null; _itemContainer.Clip = null; _itemContainer.SizeChanged -= new SizeChangedEventHandler(ItemContainerSizeChanged); _itemContainer.SelectionChanged -= new SelectionChangedEventHandler(ItemContainerSelectionChanged); _itemContainer.ClearValue(Selector.SelectedIndexProperty); _itemContainer.ClearValue(Selector.SelectedItemProperty); } base.OnApplyTemplate(); SeriesArea = GetTemplateChild(SeriesAreaName) as Panel; if (null != SeriesArea) { _seriesAreaChildrenListAdapter.TargetList = SeriesArea.Children; _seriesAreaChildrenListAdapter.Populate(); } _itemContainer = GetTemplateChild(ItemContainerName) as DelegatingListBox; if (null != _itemContainer) { _itemContainer.PrepareContainerForItem = PrepareContainerForItem; _itemContainer.ClearContainerForItem = ClearContainerForItem; _itemContainer.ItemsSource = DataItems; _itemContainer.Clip = _clipGeometry; _itemContainer.SizeChanged += new SizeChangedEventHandler(ItemContainerSizeChanged); _itemContainer.SelectionChanged += new SelectionChangedEventHandler(ItemContainerSelectionChanged); _itemContainer.SetBinding(Selector.SelectedIndexProperty, new Binding("SelectedIndex") { Source = this, Mode = BindingMode.TwoWay }); _itemContainer.SetBinding(Selector.SelectedItemProperty, new Binding("SelectedItem") { Source = this, Mode = BindingMode.TwoWay, Converter = new SelectedItemToDataItemConverter(DataItems) }); } // Synchronize selection state with new ItemContainer OnSelectionModeChanged(SeriesSelectionMode.None, SelectionMode); SelectedItemsCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// /// Prepares the specified element to display the specified item. /// /// The element used to display the specified item. /// The item to display. private void PrepareContainerForItem(DependencyObject element, object item) { DataItem dataItem = (DataItem)item; DataPoint dataPoint = CreateDataPoint(); dataItem.DataPoint = dataPoint; dataPoint.DataContext = dataItem.Value; dataPoint.SetBinding(DataPoint.DependentValueProperty, dataItem.SeriesDefinition.DependentValueBinding); dataPoint.SetBinding(DataPoint.IndependentValueProperty, dataItem.SeriesDefinition.IndependentValueBinding); dataPoint.SetBinding(DataPoint.StyleProperty, new Binding("ActualDataPointStyle") { Source = dataItem.SeriesDefinition }); dataPoint.DependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointDependentValueChanged); dataPoint.ActualDependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointActualDependentValueChanged); dataPoint.IndependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointIndependentValueChanged); dataPoint.ActualIndependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointActualIndependentValueChanged); dataPoint.StateChanged += new RoutedPropertyChangedEventHandler(DataPointStateChanged); dataPoint.DefinitionSeriesIsSelectionEnabledHandling = true; ContentControl container = (ContentControl)element; dataItem.Container = container; Binding selectionEnabledBinding = new Binding("SelectionMode") { Source = this, Converter = new SelectionModeToSelectionEnabledConverter() }; container.SetBinding(ContentControl.IsTabStopProperty, selectionEnabledBinding); dataPoint.SetBinding(DataPoint.IsSelectionEnabledProperty, selectionEnabledBinding); dataPoint.SetBinding(DataPoint.IsSelectedProperty, new Binding("IsSelected") { Source = container, Mode = BindingMode.TwoWay }); dataPoint.Visibility = Visibility.Collapsed; dataPoint.State = DataPointState.Showing; PrepareDataPoint(dataPoint); container.Content = dataPoint; } /// /// Undoes the effects of the PrepareContainerForItemOverride method. /// /// The container element. /// The item to display. private void ClearContainerForItem(DependencyObject element, object item) { DataItem dataItem = (DataItem)item; DataPoint dataPoint = dataItem.DataPoint; dataPoint.DependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointDependentValueChanged); dataPoint.ActualDependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointActualDependentValueChanged); dataPoint.IndependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointIndependentValueChanged); dataPoint.ActualIndependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointActualIndependentValueChanged); dataPoint.StateChanged -= new RoutedPropertyChangedEventHandler(DataPointStateChanged); dataPoint.ClearValue(DataPoint.DependentValueProperty); dataPoint.ClearValue(DataPoint.IndependentValueProperty); dataPoint.ClearValue(DataPoint.StyleProperty); dataPoint.ClearValue(DataPoint.IsSelectionEnabledProperty); dataPoint.ClearValue(DataPoint.IsSelectedProperty); ContentControl container = (ContentControl)dataItem.Container; container.ClearValue(ContentControl.IsTabStopProperty); dataPoint.DataContext = null; } /// /// Prepares a DataPoint for use. /// /// DataPoint instance. protected virtual void PrepareDataPoint(DataPoint dataPoint) { } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected abstract DataPoint CreateDataPoint(); /// /// Provides an internally-accessible wrapper for calling CreateDataPoint. /// /// Series-appropriate DataPoint instance. internal DataPoint InternalCreateDataPoint() { return CreateDataPoint(); } /// /// Handles the SizeChanged event of the ItemContainer. /// /// Event source. /// Event arguments. private void ItemContainerSizeChanged(object sender, SizeChangedEventArgs e) { _clipGeometry.Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height); QueueUpdateDataItemPlacement(false, false, DataItems); } /// /// Returns the DataItem corresponding to the specified DataPoint. /// /// Specified DataPoint. /// Corresponding DataItem. protected DataItem DataItemFromDataPoint(DataPoint dataPoint) { return DataItems.Where(di => di.DataPoint == dataPoint).Single(); } /// /// Handles the DependentValueChanged event of a DataPoint. /// /// Event source. /// Event arguments. private void DataPointDependentValueChanged(object sender, RoutedPropertyChangedEventArgs e) { DataPoint dataPoint = (DataPoint)sender; SeriesDefinition definition = DataItemFromDataPoint(dataPoint).SeriesDefinition; TimeSpan transitionDuration = definition.TransitionDuration; if (0 < transitionDuration.TotalMilliseconds) { dataPoint.BeginAnimation(DataPoint.ActualDependentValueProperty, "ActualDependentValue", e.NewValue, definition.TransitionDuration, definition.TransitionEasingFunction); } else { dataPoint.ActualDependentValue = e.NewValue; } } /// /// Handles the ActualDependentValueChanged event of a DataPoint. /// /// Event source. /// Event arguments. private void DataPointActualDependentValueChanged(object sender, RoutedPropertyChangedEventArgs e) { DataPoint dataPoint = (DataPoint)sender; QueueUpdateDataItemPlacement(true, false, DataItems.Where(di => di.DataPoint == dataPoint)); } /// /// Handles the IndependentValueChanged event of a DataPoint. /// /// Event source. /// Event arguments. private void DataPointIndependentValueChanged(object sender, RoutedPropertyChangedEventArgs e) { DataPoint dataPoint = (DataPoint)sender; SeriesDefinition definition = DataItemFromDataPoint(dataPoint).SeriesDefinition; TimeSpan transitionDuration = definition.TransitionDuration; if (0 < transitionDuration.TotalMilliseconds) { dataPoint.BeginAnimation(DataPoint.ActualIndependentValueProperty, "ActualIndependentValue", e.NewValue, definition.TransitionDuration, definition.TransitionEasingFunction); } else { dataPoint.ActualIndependentValue = e.NewValue; } } /// /// Handles the ActualIndependentValueChanged event of a DataPoint. /// /// Event source. /// Event arguments. private void DataPointActualIndependentValueChanged(object sender, RoutedPropertyChangedEventArgs e) { DataPoint dataPoint = (DataPoint)sender; QueueUpdateDataItemPlacement(false, true, DataItems.Where(di => di.DataPoint == dataPoint)); } /// /// Handles the StateChanged event of a DataPoint. /// /// Event source. /// Event arguments. private void DataPointStateChanged(object sender, RoutedPropertyChangedEventArgs e) { DataPoint dataPoint = (DataPoint)sender; if (DataPointState.Hidden == dataPoint.State) { DataItems.Remove(DataItems.Where(di => di.DataPoint == dataPoint).Single()); RemovedDataItems(); } } /// /// Notifies the specified axis of changes to values plotting against it. /// /// Specified axis. protected void NotifyAxisValuesChanged(IAxis axis) { if (null != axis) { IRangeConsumer rangeConsumer = axis as IRangeConsumer; if (null != rangeConsumer) { IRangeProvider rangeProvider = (IRangeProvider)this; rangeConsumer.RangeChanged(rangeProvider, new Range() /*rangeProvider.GetRange(rangeConsumer)*/); } IDataConsumer dataConsumer = axis as IDataConsumer; if (null != dataConsumer) { IDataProvider dataProvider = (IDataProvider)this; dataConsumer.DataChanged(dataProvider, null /*dataProvider.GetData(dataConsumer)*/); } } } /// /// Notifies the specified axis of changes to value margins plotting against it. /// /// Specified axis. /// Sequence of value margins that have changed. protected void NotifyValueMarginsChanged(IAxis axis, IEnumerable valueMargins) { if (null != axis) { IValueMarginConsumer valueMarginConsumer = axis as IValueMarginConsumer; if (null != valueMarginConsumer) { IValueMarginProvider valueMarginProvider = (IValueMarginProvider)this; valueMarginConsumer.ValueMarginsChanged(valueMarginProvider, valueMargins); } } } /// /// Handles the CollectionChanged event of the SeriesDefinitions collection. /// /// Event source. /// Event arguments. private void SeriesDefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { SeriesDefinitionsCollectionChanged(e.Action, e.OldItems, e.OldStartingIndex, e.NewItems, e.NewStartingIndex); } /// /// Handles the CollectionChanged event of the SeriesDefinitions collection. /// /// Type of change. /// Sequence of old items. /// Starting index of old items. /// Sequence of new items. /// Starting index of new items. protected virtual void SeriesDefinitionsCollectionChanged(NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex, IList newItems, int newStartingIndex) { if (null != oldItems) { foreach (SeriesDefinition oldDefinition in oldItems.CastWrapper()) { ISeries oldSeries = (ISeries)oldDefinition; SeriesDefinitionItemsSourceChanged(oldDefinition, oldDefinition.ItemsSource, null); _seriesDefinitionsAsISeries.Remove(oldDefinition); _legendItems.ChildCollections.Remove(oldSeries.LegendItems); UpdatePaletteProperties(oldDefinition); oldSeries.SeriesHost = null; oldDefinition.Index = -1; } } if (null != newItems) { int index = newStartingIndex; foreach (SeriesDefinition newDefinition in newItems.CastWrapper()) { ISeries newSeries = (ISeries)newDefinition; newSeries.SeriesHost = this; UpdatePaletteProperties(newDefinition); _legendItems.ChildCollections.Add(newSeries.LegendItems); _seriesDefinitionsAsISeries.Add(newDefinition); newDefinition.Index = index; SeriesDefinitionItemsSourceChanged(newDefinition, null, newDefinition.ItemsSource); index++; } } } /// /// Updates the palette properties of the specified SeriesDefinition. /// /// Specified SeriesDefinition. private void UpdatePaletteProperties(SeriesDefinition definition) { ResourceDictionary resources = null; if (null != SeriesHost) { Type dataPointType = CreateDataPoint().GetType(); using (IEnumerator enumerator = SeriesHost.GetResourceDictionariesWhere(dictionary => { Style style = dictionary["DataPointStyle"] as Style; if (null != style) { return (null != style.TargetType) && (style.TargetType.IsAssignableFrom(dataPointType)); } return false; })) { if (enumerator.MoveNext()) { resources = enumerator.Current; } } } definition.PaletteDataPointStyle = (null != resources) ? resources["DataPointStyle"] as Style : null; definition.PaletteDataShapeStyle = (null != resources) ? resources["DataShapeStyle"] as Style : null; definition.PaletteLegendItemStyle = (null != resources) ? resources["LegendItemStyle"] as Style : null; } /// /// Handles changes to the ItemsSource of a SeriesDefinition. /// /// SeriesDefinition owner. /// Old value. /// New value. internal void SeriesDefinitionItemsSourceChanged(SeriesDefinition definition, IEnumerable oldValue, IEnumerable newValue) { if (null != oldValue) { foreach (DataItem dataItem in DataItems.Where(di => di.SeriesDefinition == definition).ToArray()) { DataItems.Remove(dataItem); } RemovedDataItems(); } if (null != newValue) { // No need to add items if SeriesHost null; setting SeriesHost will take care of that if (null != SeriesHost) { AddDataItems(definition, newValue.CastWrapper(), 0); } } } /// /// Handles changes to the ItemsSource collection of a SeriesDefinition. /// /// SeriesDefinition owner. /// Type of change. /// Sequence of old items. /// Starting index of old items. /// Sequence of new items. /// Starting index of new items. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq is artificially increasing the rating.")] internal void SeriesDefinitionItemsSourceCollectionChanged(SeriesDefinition definition, NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex, IList newItems, int newStartingIndex) { if (NotifyCollectionChangedAction.Replace == action) { // Perform in-place replacements foreach (DataItem dataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (newStartingIndex <= di.Index) && (di.Index < newStartingIndex + newItems.Count))) { dataItem.Value = newItems[dataItem.Index - newStartingIndex]; } } else { if (NotifyCollectionChangedAction.Reset == action) { // Set up parameters to allow normal old/new item handling to be used Debug.Assert(null == oldItems, "Reset action with non-null oldItems."); oldItems = DataItems.Where(di => (di.SeriesDefinition == definition)).ToArray(); oldStartingIndex = 0; newItems = definition.ItemsSource.CastWrapper().ToArray(); newStartingIndex = 0; } if (null != oldItems) { // Get rid of old items foreach (DataItem oldDataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (oldStartingIndex <= di.Index) && (di.Index < oldStartingIndex + oldItems.Count))) { oldDataItem.Index = -1; if (null != oldDataItem.DataPoint) { oldDataItem.DataPoint.State = DataPointState.Hiding; } } // Adjust index of shifted items foreach (DataItem dataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (oldStartingIndex + oldItems.Count <= di.Index))) { dataItem.Index -= oldItems.Count; } } if (null != newItems) { // Adjust index of shifted items foreach (DataItem dataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (newStartingIndex <= di.Index))) { dataItem.Index += newItems.Count; } // Add new items AddDataItems(definition, newItems.CastWrapper(), newStartingIndex); } } #if DEBUG // Validate all DataItem index and value properties foreach (var group in DataItems.Where(di => 0 <= di.Index).OrderBy(di => di.Index).GroupBy(di => di.SeriesDefinition)) { object[] items = group.Key.ItemsSource.CastWrapper().ToArray(); int i = 0; foreach (DataItem dataItem in group) { Debug.Assert(i == dataItem.Index, "DataItem index mis-match."); Debug.Assert(dataItem.Value.Equals(items[i]), "DataItem value mis-match."); i++; } } #endif } /// /// Handles the ResourceDictionariesChanged event of the SeriesHost owner. /// /// Event source. /// Event arguments. private void SeriesHostResourceDictionariesChanged(object sender, EventArgs e) { foreach (SeriesDefinition definition in SeriesDefinitions) { UpdatePaletteProperties(definition); } } /// /// Creates and adds DataItems for the specified SeriesDefinition's items. /// /// Specified SeriesDefinition. /// Sequence of items. /// Starting index. private void AddDataItems(SeriesDefinition definition, IEnumerable items, int startingIndex) { int index = startingIndex; foreach (object item in items) { DataItems.Add(new DataItem(definition) { Value = item, Index = index }); index++; } // Because properties (like DependentValueBinding) may still be getting set Dispatcher.BeginInvoke((Action)AddedDataItems); } /// /// Updates the axes after DataItems have been added. /// private void AddedDataItems() { EnsureAxes(false, false, true); } /// /// Notifies the axes after DataItems have been removed. /// private void RemovedDataItems() { NotifyAxisValuesChanged(ActualIndependentAxis); NotifyAxisValuesChanged(ActualDependentAxis); } /// /// Ensures that suitable axes are present and registered. /// /// True if the dependent axis needs to be updated. /// True if the independent axis needs to be updated. /// True if both axis are to be notified unconditionally. private void EnsureAxes(bool updateDependentAxis, bool updateIndependentAxis, bool unconditionallyNotifyAxes) { foreach (SeriesDefinition definition in SeriesDefinitions) { if (null == definition.DependentValueBinding) { throw new InvalidOperationException(Properties.Resources.DefinitionSeries_EnsureAxes_MissingDependentValueBinding); } if (null == definition.IndependentValueBinding) { throw new InvalidOperationException(Properties.Resources.DefinitionSeries_EnsureAxes_MissingIndependentValueBinding); } } if ((null != SeriesHost) && DataItems.Any()) { // Ensure a dependent axis is present or updated bool changedActualDependentAxis = false; if (updateDependentAxis && (null != ActualDependentAxis)) { ActualDependentAxis.RegisteredListeners.Remove(this); ActualDependentAxis = null; } if (null == ActualDependentAxis) { ActualDependentAxis = DependentAxis ?? AcquireDependentAxis(); ActualDependentAxis.RegisteredListeners.Add(this); if (!SeriesHost.Axes.Contains(ActualDependentAxis)) { SeriesHost.Axes.Add(ActualDependentAxis); } changedActualDependentAxis = true; } // Ensure an independent axis is present or updated bool changedActualIndependentAxis = false; if (updateIndependentAxis && (null != ActualIndependentAxis)) { ActualIndependentAxis.RegisteredListeners.Remove(this); ActualIndependentAxis = null; } if (null == ActualIndependentAxis) { ActualIndependentAxis = IndependentAxis ?? AcquireIndependentAxis(); ActualIndependentAxis.RegisteredListeners.Add(this); if (!SeriesHost.Axes.Contains(ActualIndependentAxis)) { SeriesHost.Axes.Add(ActualIndependentAxis); } changedActualIndependentAxis = true; } // Queue an update if necessary or requested if (changedActualDependentAxis || changedActualIndependentAxis || unconditionallyNotifyAxes) { QueueUpdateDataItemPlacement(changedActualDependentAxis || unconditionallyNotifyAxes, changedActualIndependentAxis || unconditionallyNotifyAxes, DataItems); } } } /// /// Acquires a dependent axis suitable for use with the data values of the series. /// /// Axis instance. protected abstract IAxis AcquireDependentAxis(); /// /// Acquires an independent axis suitable for use with the data values of the series. /// /// Axis instance. protected abstract IAxis AcquireIndependentAxis(); /// /// Handles notification of the invalidation of an axis. /// /// Invalidated axis. void IAxisListener.AxisInvalidated(IAxis axis) { QueueUpdateDataItemPlacement(false, false, DataItems); } /// /// Queues an update of DataItem placement for the next update opportunity. /// /// True if the dependent axis values have changed. /// True if the independent axis values have changed. /// Sequence of DataItems to update. private void QueueUpdateDataItemPlacement(bool dependentAxisValuesChanged, bool independentAxisValuesChanged, IEnumerable dataItems) { _queueUpdateDataItemPlacement_DependentAxisValuesChanged |= dependentAxisValuesChanged; _queueUpdateDataItemPlacement_IndependentAxisValuesChanged |= independentAxisValuesChanged; _queueUpdateDataItemPlacement_DataItems.AddRange(dataItems); InvalidateArrange(); } /// /// Called when the control needs to arrange its children. /// /// Bounds to arrange within. /// Arranged size. /// /// Used as a good place to dequeue queued work. /// protected override Size ArrangeOverride(Size arrangeBounds) { Size arrangedSize = base.ArrangeOverride(arrangeBounds); if (_queueUpdateDataItemPlacement_DependentAxisValuesChanged) { NotifyAxisValuesChanged(ActualDependentAxis); _queueUpdateDataItemPlacement_DependentAxisValuesChanged = false; } if (_queueUpdateDataItemPlacement_IndependentAxisValuesChanged) { NotifyAxisValuesChanged(ActualIndependentAxis); _queueUpdateDataItemPlacement_IndependentAxisValuesChanged = false; } UpdateDataItemPlacement(_queueUpdateDataItemPlacement_DataItems.Distinct()); _queueUpdateDataItemPlacement_DataItems.Clear(); return arrangedSize; } /// /// Updates the placement of the DataItems (data points) of the series. /// /// DataItems in need of an update. protected abstract void UpdateDataItemPlacement(IEnumerable dataItems); /// /// Returns the range for the data points of the series. /// /// Consumer of the range. /// Range of values. Range IRangeProvider.GetRange(IRangeConsumer rangeConsumer) { return IRangeProviderGetRange(rangeConsumer); } /// /// Returns the range for the data points of the series. /// /// Consumer of the range. /// Range of values. protected virtual Range IRangeProviderGetRange(IRangeConsumer rangeConsumer) { if (rangeConsumer == ActualIndependentAxis) { if (ActualIndependentAxis.CanPlot(0.0)) { return IndependentValueGroups .Select(g => ValueHelper.ToDouble(g.IndependentValue)) .Where(d => ValueHelper.CanGraph(d)) .DefaultIfEmpty() .CastWrapper() .GetRange(); } else { return IndependentValueGroups .Select(g => ValueHelper.ToDateTime(g.IndependentValue)) .DefaultIfEmpty() .CastWrapper() .GetRange(); } } throw new NotSupportedException(); } /// /// Returns the value margins for the data points of the series. /// /// Consumer of the value margins. /// Sequence of value margins. IEnumerable IValueMarginProvider.GetValueMargins(IValueMarginConsumer valueMarginConsumer) { return IValueMarginProviderGetValueMargins(valueMarginConsumer); } /// /// Returns the value margins for the data points of the series. /// /// Consumer of the value margins. /// Sequence of value margins. protected virtual IEnumerable IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) { throw new NotImplementedException(); } /// /// Returns the data for the data points of the series. /// /// Consumer of the data. /// Sequence of data. IEnumerable IDataProvider.GetData(IDataConsumer dataConsumer) { return IDataProviderGetData(dataConsumer); } /// /// Returns the data for the data points of the series. /// /// Consumer of the data. /// Sequence of data. protected virtual IEnumerable IDataProviderGetData(IDataConsumer dataConsumer) { if (dataConsumer == ActualIndependentAxis) { return IndependentValueGroups.Select(cg => cg.IndependentValue).Distinct(); } throw new NotImplementedException(); } /// /// Gets a sequence of IndependentValueGroups. /// protected virtual IEnumerable IndependentValueGroups { get { return DataItems .GroupBy(di => di.ActualIndependentValue) .Select(g => new IndependentValueGroup(g.Key, g.OrderBy(di => di.SeriesDefinition.Index))); } } /// /// Gets a sequence of IndependentValueGroups ordered by independent value. /// protected IEnumerable IndependentValueGroupsOrderedByIndependentValue { get { return IndependentValueGroups .OrderBy(g => g.IndependentValue); } } /// /// Gets a sequence of sequences of the dependent values associated with each independent value. /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nesting reflects the actual hierarchy of the data.")] protected IEnumerable> IndependentValueDependentValues { get { return IndependentValueGroups .Select(g => { g.Denominator = IsStacked100 ? g.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.ActualDependentValue))) : 1; if (0 == g.Denominator) { g.Denominator = 1; } return g; }) .Select(g => g.DataItems .Select(di => ValueHelper.ToDouble(di.ActualDependentValue) * (IsStacked100 ? (100 / g.Denominator) : 1))); } } /// /// Represents an independent value and the dependent values that are associated with it. /// protected class IndependentValueGroup { /// /// Initializes a new instance of the IndependentValueGroup class. /// /// Independent value. /// Associated DataItems. public IndependentValueGroup(object independentValue, IEnumerable dataItems) { IndependentValue = independentValue; DataItems = dataItems; } /// /// Gets the independent value. /// public object IndependentValue { get; private set; } /// /// Gets a sequence of DataItems associated with the independent value. /// public IEnumerable DataItems { get; private set; } /// /// Gets or sets the denominator to use when computing with this instance. /// /// /// Exists here purely to simplify the the corresponding algorithm. /// public double Denominator { get; set; } } /// /// Represents a single data value from a SeriesDefinition's ItemsSource. /// protected class DataItem { /// /// Stores a reference to a shared BindingHelper instance. /// private static readonly BindingHelper _bindingHelper = new BindingHelper(); /// /// Initializes a new instance of the DataItem class. /// /// SeriesDefinition owner. public DataItem(SeriesDefinition seriesDefinition) { SeriesDefinition = seriesDefinition; CenterPoint = new Point(double.NaN, double.NaN); } /// /// Gets the SeriesDefinition owner of the DataItem. /// public SeriesDefinition SeriesDefinition { get; private set; } /// /// Gets or sets the value of the DataItem. /// public object Value { get { return _value; } set { _value = value; if (null != DataPoint) { DataPoint.DataContext = value; } } } /// /// Stores the value of the DataItem. /// private object _value; /// /// Gets or sets the index of the DataItem. /// public int Index { get; set; } /// /// Gets or sets the DataPoint associated with the DataItem. /// public DataPoint DataPoint { get; set; } /// /// Gets or sets the container for the DataPoint within its parent ItemsControl. /// public UIElement Container { get; set; } /// /// Gets the ActualDependentValue of the DataPoint (or its equivalent). /// public IComparable ActualDependentValue { get { if (null != DataPoint) { return DataPoint.ActualDependentValue; } else { return (IComparable)_bindingHelper.EvaluateBinding(SeriesDefinition.DependentValueBinding, Value); } } } /// /// Gets the ActualIndependentValue of the DataPoint (or its equivalent). /// public object ActualIndependentValue { get { if (null != DataPoint) { return DataPoint.ActualIndependentValue; } else { return _bindingHelper.EvaluateBinding(SeriesDefinition.IndependentValueBinding, Value); } } } /// /// Gets or sets the ActualDependentValue of the DataPoint after adjusting for applicable stacking. /// public double ActualStackedDependentValue { get; set; } /// /// Gets or sets the center-point of the DataPoint in plot area coordinates (if relevant). /// public Point CenterPoint { get; set; } } /// /// Provides an easy way to evaluate a Binding against a source instance. /// private class BindingHelper : FrameworkElement { /// /// Initializes a new instance of the BindingHelper class. /// public BindingHelper() { } /// /// Identifies the Result dependency property. /// private static readonly DependencyProperty ResultProperty = DependencyProperty.Register("Result", typeof(object), typeof(BindingHelper), null); /// /// Evaluates a Binding against a source instance. /// /// Binding to evaluate. /// Source instance. /// Result of Binding on source instance. public object EvaluateBinding(Binding binding, object instance) { DataContext = instance; SetBinding(ResultProperty, binding); object result = GetValue(ResultProperty); ClearValue(ResultProperty); DataContext = null; return result; } } /// /// Converts from a selected item to the corresponding DataItem. /// private class SelectedItemToDataItemConverter : IValueConverter { /// /// Stores a reference to the DataItem collection. /// private ObservableCollection _dataItems; /// /// Initializes a new instance of the SelectedItemToDataItemConverter class. /// /// Collection of DataItems. public SelectedItemToDataItemConverter(ObservableCollection dataItems) { _dataItems = dataItems; } /// /// Converts a value. /// /// The value produced by the binding source. /// The type of the binding target property. /// The converter parameter to use. /// The culture to use in the converter. /// Converted value. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return _dataItems.Where(di => di.Value == value).FirstOrDefault(); } /// /// Converts a value back. /// /// The value produced by the binding source. /// The type of the binding target property. /// The converter parameter to use. /// The culture to use in the converter. /// Converted value. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { DataItem dataItem = value as DataItem; return (null != dataItem) ? dataItem.Value : null; } } /// /// Converts from a SeriesSelectionMode to a true/false value indicating whether selection is enabled. /// private class SelectionModeToSelectionEnabledConverter : IValueConverter { /// /// Initializes a new instance of the SelectionModeToSelectionEnabledConverter class. /// public SelectionModeToSelectionEnabledConverter() { } /// /// Converts a value. /// /// The value produced by the binding source. /// The type of the binding target property. /// The converter parameter to use. /// The culture to use in the converter. /// Converted value. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { bool isSelectionEnabled = false; if (value is SeriesSelectionMode) { isSelectionEnabled = !(SeriesSelectionMode.None == (SeriesSelectionMode)value); } return isSelectionEnabled; } /// /// Converts a value back. /// /// The value produced by the binding source. /// The type of the binding target property. /// The converter parameter to use. /// The culture to use in the converter. /// Converted value. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } /// /// Gets the axes for the series as a series host. /// [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")] ObservableCollection ISeriesHost.Axes { get { throw new NotImplementedException(); } } /// /// Gets the series for the series as a series host. /// ObservableCollection ISeriesHost.Series { get { return _seriesDefinitionsAsISeries; } } /// /// Gets the foreground elements for the series as a series host. /// [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")] ObservableCollection ISeriesHost.ForegroundElements { get { throw new NotImplementedException(); } } /// /// Gets the background elements for the series as a series host. /// [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")] ObservableCollection ISeriesHost.BackgroundElements { get { throw new NotImplementedException(); } } /// /// Gets a IResourceDictionaryDispenser for the series as a series host. /// /// Predicate function. /// Sequence of ResourceDictionaries. IEnumerator IResourceDictionaryDispenser.GetResourceDictionariesWhere(Func predicate) { throw new NotImplementedException(); } /// /// Event that is triggered when the available ResourceDictionaries change. /// [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")] event EventHandler IResourceDictionaryDispenser.ResourceDictionariesChanged { add { throw new NotImplementedException(); } remove { throw new NotImplementedException(); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/IRequireGlobalSeriesIndex.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Defines methods on classes that contain a global index. /// /// Preview public interface IRequireGlobalSeriesIndex { /// /// Occurs when a global series index changes. /// /// The global index that has changed. /// void GlobalSeriesIndexChanged(int? globalIndex); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/ISeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.ObjectModel; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a series in a chart. /// public interface ISeries : IRequireSeriesHost { /// /// Gets a list of the legend items associated with the object. /// ObservableCollection LegendItems { get; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/LabeledPieSeries.cs ================================================ using System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Charting { public class LabeledPieSeries : PieSeries { /// /// PieChartLabelStyle DP - The style to be applied to each PieChartLabel. /// public Style PieChartLabelStyle { get { return (Style)this.GetValue(PieChartLabelStyleProperty); } set { this.SetValue(PieChartLabelStyleProperty, value); } } public static readonly DependencyProperty PieChartLabelStyleProperty = DependencyProperty.Register("PieChartLabelStyle", typeof(Style), typeof(LabeledPieSeries)); /// /// PieChartLabelItemTemplate DP - The DataTemplate to be applied to each PieChartLabel. /// public DataTemplate PieChartLabelItemTemplate { get { return (DataTemplate)this.GetValue(PieChartLabelItemTemplateProperty); } set { this.SetValue(PieChartLabelItemTemplateProperty, value); } } public static readonly DependencyProperty PieChartLabelItemTemplateProperty = DependencyProperty.Register("PieChartLabelItemTemplate", typeof(DataTemplate), typeof(LabeledPieSeries)); /// /// LabelDisplayMode DP - Controls the mode in which the labels should appear. /// public DisplayMode LabelDisplayMode { get { return (DisplayMode)this.GetValue(LabelDisplayModeProperty); } set { this.SetValue(LabelDisplayModeProperty, value); } } public static readonly DependencyProperty LabelDisplayModeProperty = DependencyProperty.Register("LabelDisplayMode", typeof(DisplayMode), typeof(LabeledPieSeries), new PropertyMetadata(DisplayMode.AutoMixed)); /// /// Adds the labels to each of its PieDataPoints. /// protected override void OnAfterUpdateDataPoints() { var chart = this.SeriesHost as Chart; if (chart != null) { Canvas labelArea = chart.Template.FindName("LabelArea_PART", chart) as Canvas; if (labelArea != null && this.ItemsSource != null) { foreach (object dataItem in this.ItemsSource) { PieDataPoint pieDataPoint = this.GetDataPoint(dataItem) as PieDataPoint; if (pieDataPoint != null) { this.AddLabelPieDataPoint(pieDataPoint, labelArea); } } } } } /// /// Creates a new PieChartLabel control and adds it to the "LabelArea_PART" canvas. /// /// PieDataPoint that corresponds to PieChartLabel. /// The style to be applied to the PieChartLabel. /// The canvas where PieChartLabel will be added. private void AddLabelPieDataPoint(PieDataPoint pieDataPoint, Canvas labelArea) { PieChartLabel label = new PieChartLabel(); label.Style = this.PieChartLabelStyle; label.ContentTemplate = this.PieChartLabelItemTemplate; Binding contentBinding = new Binding("DataContext") { Source = pieDataPoint }; label.SetBinding(ContentControl.ContentProperty, contentBinding); Binding dataContextBinding = new Binding("DataContext") { Source = pieDataPoint }; label.SetBinding(ContentControl.DataContextProperty, contentBinding); Binding formattedRatioBinding = new Binding("FormattedRatio") { Source = pieDataPoint }; label.SetBinding(PieChartLabel.FormattedRatioProperty, formattedRatioBinding); Binding visibilityBinding = new Binding("Ratio") { Source = pieDataPoint, Converter = new DoubleToVisibilityConverter() }; label.SetBinding(PieChartLabel.VisibilityProperty, visibilityBinding); Binding geometryBinding = new Binding("Geometry") { Source = pieDataPoint }; label.SetBinding(PieChartLabel.GeometryProperty, geometryBinding); Binding displayModeBinding = new Binding("LabelDisplayMode") { Source = this }; label.SetBinding(PieChartLabel.DisplayModeProperty, displayModeBinding); labelArea.Children.Add(label); pieDataPoint.Unloaded += delegate { labelArea.Children.Remove(label); }; pieDataPoint.Loaded += delegate { if (label.Parent == null) { labelArea.Children.Add(label); } }; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/LegendItem.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents an item used by a Series in the Legend of a Chart. /// /// Preview public class LegendItem : ContentControl { /// /// Gets or sets the owner of the LegendItem. /// public object Owner { get; set; } /// /// Initializes the static members of the LegendItem class. /// static LegendItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(LegendItem), new FrameworkPropertyMetadata(typeof(LegendItem))); } /// /// Initializes a new instance of the LegendItem class. /// public LegendItem() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/LineAreaBaseSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Windows.Controls.DataVisualization.Collections; namespace System.Windows.Controls.DataVisualization.Charting { /// /// A base class that contains methods used by both the line and area series. /// /// The type of data point used by the series. public abstract class LineAreaBaseSeries : DataPointSingleSeriesWithAxes where T : DataPoint, new() { #region public IRangeAxis DependentRangeAxis /// /// Gets or sets the dependent range axis. /// public IRangeAxis DependentRangeAxis { get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register( "DependentRangeAxis", typeof(IRangeAxis), typeof(LineAreaBaseSeries), new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); /// /// DependentRangeAxisProperty property changed handler. /// /// LineAreaBaseSeries that changed its DependentRangeAxis. /// Event arguments. private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LineAreaBaseSeries source = (LineAreaBaseSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnDependentRangeAxisPropertyChanged(newValue); } /// /// DependentRangeAxisProperty property changed handler. /// /// New value. private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; } #endregion public IRangeAxis DependentRangeAxis #region public IAxis IndependentAxis /// /// Gets or sets the independent range axis. /// public IAxis IndependentAxis { get { return GetValue(IndependentAxisProperty) as IAxis; } set { SetValue(IndependentAxisProperty, value); } } /// /// Identifies the IndependentAxis dependency property. /// [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] public static readonly DependencyProperty IndependentAxisProperty = DependencyProperty.Register( "IndependentAxis", typeof(IAxis), typeof(LineAreaBaseSeries), new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); /// /// IndependentAxisProperty property changed handler. /// /// LineAreaBaseSeries that changed its IndependentAxis. /// Event arguments. private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LineAreaBaseSeries source = (LineAreaBaseSeries)d; IAxis newValue = (IAxis)e.NewValue; source.OnIndependentAxisPropertyChanged(newValue); } /// /// IndependentAxisProperty property changed handler. /// /// New value. private void OnIndependentAxisPropertyChanged(IAxis newValue) { this.InternalIndependentAxis = (IAxis)newValue; } #endregion public IAxis IndependentAxis /// /// Gets data points collection sorted by independent value. /// internal OrderedMultipleDictionary DataPointsByIndependentValue { get; private set; } /// /// Gets the independent axis as a range axis. /// public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } /// /// Gets the dependent axis as a range axis. /// public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } /// /// Initializes a new instance of the LineAreaBaseSeries class. /// protected LineAreaBaseSeries() { DataPointsByIndependentValue = new OrderedMultipleDictionary( false, (left, right) => left.CompareTo(right), (leftDataPoint, rightDataPoint) => RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint))); } /// /// Creates a DataPoint for determining the line color. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); if (null != PlotArea) { Grid grid = new Grid(); DataPoint dataPoint = CreateDataPoint(); dataPoint.Visibility = Visibility.Collapsed; dataPoint.Loaded += delegate { dataPoint.SetStyle(ActualDataPointStyle); Background = dataPoint.Background; if (null != PlotArea) { PlotArea.Children.Remove(grid); } }; grid.Children.Add(dataPoint); PlotArea.Children.Add(grid); } } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected override void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { base.OnDataPointsChanged(newDataPoints, oldDataPoints); if (ActualIndependentAxis is IRangeAxis) { foreach (DataPoint dataPoint in oldDataPoints) { DataPointsByIndependentValue.Remove((IComparable)dataPoint.IndependentValue, dataPoint); } foreach (DataPoint dataPoint in newDataPoints) { DataPointsByIndependentValue.Add((IComparable)dataPoint.IndependentValue, dataPoint); } } } /// /// This method executes after all data points have been updated. /// protected override void OnAfterUpdateDataPoints() { if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null) { UpdateShape(); } } /// /// Repositions line data point in the sorted collection if the actual /// independent axis is a range axis. /// /// The data point that has changed. /// The old value. /// The new value. protected override void OnDataPointIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { if (ActualIndependentAxis is IRangeAxis && !oldValue.Equals(newValue)) { bool removed = DataPointsByIndependentValue.Remove((IComparable)oldValue, dataPoint); if (removed) { DataPointsByIndependentValue.Add((IComparable)newValue, dataPoint); } } base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue); } /// /// Creates a new line data point. /// /// A line data point. protected override DataPoint CreateDataPoint() { return new T(); } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// protected override IEnumerator GetResourceDictionaryEnumeratorFromHost() { return GetResourceDictionaryWithTargetType(SeriesHost, typeof(T), true); } /// /// Updates the visual representation of the data point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; if (ValueHelper.CanGraph(maximum)) { double x = ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value; double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value; if (ValueHelper.CanGraph(x) && ValueHelper.CanGraph(y)) { dataPoint.Visibility = Visibility.Visible; double coordinateY = Math.Round(maximum - (y + (dataPoint.ActualHeight / 2))); Canvas.SetTop(dataPoint, coordinateY); double coordinateX = Math.Round(x - (dataPoint.ActualWidth / 2)); Canvas.SetLeft(dataPoint, coordinateX); } else { dataPoint.Visibility = Visibility.Collapsed; } } if (!UpdatingDataPoints) { UpdateShape(); } } /// /// Updates the Series shape object. /// protected virtual void UpdateShape() { double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; Func createPoint = dataPoint => new Point( ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value, maximum - ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value); IEnumerable points = Enumerable.Empty(); if (ValueHelper.CanGraph(maximum)) { if (ActualIndependentAxis is IRangeAxis) { points = DataPointsByIndependentValue.Select(createPoint); } else { points = ActiveDataPoints .Select(createPoint) .OrderBy(point => point.X); } } UpdateShapeFromPoints(points); } /// /// Updates the Series shape object from a collection of Points. /// /// Collection of Points. protected abstract void UpdateShapeFromPoints(IEnumerable points); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/LineSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in X/Y /// line format. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(LineDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = "PolylineStyle", StyleTargetType = typeof(Polyline))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] public partial class LineSeries : LineAreaBaseSeries { #region public PointCollection Points /// /// Gets the collection of points that make up the line. /// public PointCollection Points { get { return GetValue(PointsProperty) as PointCollection; } private set { SetValue(PointsProperty, value); } } /// /// Identifies the Points dependency property. /// public static readonly DependencyProperty PointsProperty = DependencyProperty.Register( "Points", typeof(PointCollection), typeof(LineSeries), null); #endregion public PointCollection Points #region public Style PolylineStyle /// /// Gets or sets the style of the Polyline object that follows the data /// points. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches System.Windows.Shapes.Polyline.")] public Style PolylineStyle { get { return GetValue(PolylineStyleProperty) as Style; } set { SetValue(PolylineStyleProperty, value); } } /// /// Identifies the PolylineStyle dependency property. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches System.Windows.Shapes.Polyline.")] public static readonly DependencyProperty PolylineStyleProperty = DependencyProperty.Register( "PolylineStyle", typeof(Style), typeof(LineSeries), null); #endregion public Style PolylineStyle /// /// Initializes the static members of the LineSeries class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static LineSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(LineSeries), new FrameworkPropertyMetadata(typeof(LineSeries))); } /// /// Initializes a new instance of the LineSeries class. /// public LineSeries() { } /// /// Acquire a horizontal linear axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// /// Updates the Series shape object from a collection of Points. /// /// Collection of Points. protected override void UpdateShapeFromPoints(IEnumerable points) { if (points.Any()) { PointCollection pointCollection = new PointCollection(); foreach (Point point in points) { pointCollection.Add(point); } Points = pointCollection; } else { Points = null; } } } } #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/PieSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Data; using System.Windows.Media; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in pie /// format. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(PieDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] public partial class PieSeries : DataPointSeries, IResourceDictionaryDispenser, IRequireGlobalSeriesIndex { #region public Collection Palette /// /// Gets or sets a palette of ResourceDictionaries used by the series. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Want to allow this to be set from XAML.")] public Collection Palette { get { return GetValue(PaletteProperty) as Collection; } set { SetValue(PaletteProperty, value); } } /// /// Identifies the Palette dependency property. /// public static readonly DependencyProperty PaletteProperty = DependencyProperty.Register( "Palette", typeof(Collection), typeof(Series), new PropertyMetadata(OnPalettePropertyChanged)); /// /// PaletteProperty property changed handler. /// /// Parent that changed its Palette. /// Event arguments. private static void OnPalettePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PieSeries source = d as PieSeries; Collection newValue = e.NewValue as Collection; source.OnPalettePropertyChanged(newValue); } /// /// PaletteProperty property changed handler. /// /// New value. private void OnPalettePropertyChanged(Collection newValue) { ResourceDictionaryDispenser.ResourceDictionaries = newValue; } #endregion public Collection Palette /// /// The pie data point style enumerator. /// private IEnumerator _resourceDictionaryEnumerator; /// /// Initializes the static members of the PieSeries class. /// static PieSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(PieSeries), new FrameworkPropertyMetadata(typeof(PieSeries))); } /// /// Initializes a new instance of the PieSeries class. /// public PieSeries() { this.ResourceDictionaryDispenser = new ResourceDictionaryDispenser(); ResourceDictionaryDispenser.ResourceDictionariesChanged += delegate { OnResourceDictionariesChanged(EventArgs.Empty); }; } /// /// Invokes the ResourceDictionariesChanged event. /// /// Event arguments. protected virtual void OnResourceDictionariesChanged(EventArgs e) { // Update with new styles Refresh(); // Forward event on to listeners EventHandler handler = ResourceDictionariesChanged; if (null != handler) { handler.Invoke(this, e); } } /// /// A dictionary that links data points to their legend items. /// private Dictionary _dataPointLegendItems = new Dictionary(); /// /// Accepts a ratio of a full rotation, the x and y length and returns /// the 2D point using trigonometric functions. /// /// The ratio of a full rotation [0..1]. /// The x radius. /// The y radius. /// The corresponding 2D point. private static Point ConvertRatioOfRotationToPoint(double ratio, double radiusX, double radiusY) { double radians = (((ratio * 360) - 90) * (Math.PI / 180)); return new Point(radiusX * Math.Cos(radians), radiusY * Math.Sin(radians)); } /// /// Creates a legend item for each data point. /// /// The data point added. protected override void AddDataPoint(DataPoint dataPoint) { base.AddDataPoint(dataPoint); PieDataPoint pieDataPoint = (PieDataPoint)dataPoint; int index = ActiveDataPoints.IndexOf(dataPoint) + 1; LegendItem legendItem = CreatePieLegendItem(dataPoint, index); // Grab a style enumerator if we don't have one already. if (_resourceDictionaryEnumerator == null) { _resourceDictionaryEnumerator = GetResourceDictionaryWithTargetType(this, typeof(PieDataPoint), true); } if (_resourceDictionaryEnumerator.MoveNext()) { ResourceDictionary paletteResources = _resourceDictionaryEnumerator.Current; pieDataPoint.PaletteResources = paletteResources; pieDataPoint.Resources.MergedDictionaries.Add(paletteResources); } else { pieDataPoint.PaletteResources = null; } pieDataPoint.ActualDataPointStyle = DataPointStyle ?? pieDataPoint.Resources[DataPointStyleName] as Style; pieDataPoint.SetBinding(PieDataPoint.StyleProperty, new Binding(PieDataPoint.ActualDataPointStyleName) { Source = pieDataPoint }); pieDataPoint.ActualLegendItemStyle = LegendItemStyle ?? (pieDataPoint.Resources[LegendItemStyleName] as Style); legendItem.SetBinding(LegendItem.StyleProperty, new Binding(ActualLegendItemStyleName) { Source = pieDataPoint }); _dataPointLegendItems[dataPoint] = legendItem; LegendItems.Add(legendItem); UpdateLegendItemIndexes(); } /// /// Removes data point's legend item when the data point is removed. /// /// The data point to remove. protected override void RemoveDataPoint(DataPoint dataPoint) { base.RemoveDataPoint(dataPoint); if (dataPoint != null) { LegendItem legendItem = _dataPointLegendItems[dataPoint]; _dataPointLegendItems.Remove(dataPoint); LegendItems.Remove(legendItem); UpdateLegendItemIndexes(); } } /// /// Creates a data point. /// /// A data point. protected override DataPoint CreateDataPoint() { return new PieDataPoint(); } /// /// Gets the active pie data points. /// private IEnumerable ActivePieDataPoints { get { return ActiveDataPoints.OfType(); } } /// /// Updates all ratios before data points are updated. /// protected override void OnBeforeUpdateDataPoints() { UpdateRatios(); base.OnBeforeUpdateDataPoints(); } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected override void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { UpdateDataPoints(newDataPoints); base.OnDataPointsChanged(newDataPoints, oldDataPoints); } /// /// Updates the indexes of all legend items when a change is made to the collection. /// private void UpdateLegendItemIndexes() { int index = 0; foreach (DataPoint dataPoint in ActiveDataPoints) { LegendItem legendItem = _dataPointLegendItems[dataPoint]; legendItem.Content = dataPoint.IndependentValue ?? (index + 1); index++; } } /// /// Updates the ratios of each data point. /// private void UpdateRatios() { double sum = ActivePieDataPoints.Select(pieDataPoint => Math.Abs(ValueHelper.ToDouble(pieDataPoint.DependentValue))).Sum(); // Priming the loop by calculating initial value of // offset ratio and its corresponding points. double offsetRatio = 0; foreach (PieDataPoint dataPoint in ActivePieDataPoints) { double dependentValue = Math.Abs(ValueHelper.ToDouble(dataPoint.DependentValue)); double ratio = dependentValue / sum; if (!ValueHelper.CanGraph(ratio)) { ratio = 0.0; } dataPoint.Ratio = ratio; dataPoint.OffsetRatio = offsetRatio; offsetRatio += ratio; } } /// /// Updates a data point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { PieDataPoint pieDataPoint = (PieDataPoint) dataPoint; pieDataPoint.Width = ActualWidth; pieDataPoint.Height = ActualHeight; UpdatePieDataPointGeometry(pieDataPoint, ActualWidth, ActualHeight); Canvas.SetLeft(pieDataPoint, 0); Canvas.SetTop(pieDataPoint, 0); } /// /// Updates the PieDataPoint's Geometry property. /// /// PieDataPoint instance. /// PlotArea width. /// PlotArea height. internal static void UpdatePieDataPointGeometry(PieDataPoint pieDataPoint, double plotAreaWidth, double plotAreaHeight) { double diameter = (plotAreaWidth < plotAreaHeight) ? plotAreaWidth : plotAreaHeight; diameter *= 0.95; double plotAreaRadius = diameter / 2; double maxDistanceFromCenter = 0.0; double sliceRadius = plotAreaRadius - maxDistanceFromCenter; Point translatePoint = new Point(plotAreaWidth / 2, plotAreaHeight / 2); if (pieDataPoint.ActualRatio == 1) { foreach (DependencyProperty dependencyProperty in new DependencyProperty[] { PieDataPoint.GeometryProperty, PieDataPoint.GeometrySelectionProperty, PieDataPoint.GeometryHighlightProperty }) { Geometry geometry = new EllipseGeometry { Center = translatePoint, RadiusX = sliceRadius, RadiusY = sliceRadius }; pieDataPoint.SetValue(dependencyProperty, geometry); } } else { if (pieDataPoint.ActualRatio == 0.0) { pieDataPoint.Geometry = null; pieDataPoint.GeometryHighlight = null; pieDataPoint.GeometrySelection = null; } else { double ratio = pieDataPoint.ActualRatio; double offsetRatio = pieDataPoint.ActualOffsetRatio; double currentRatio = offsetRatio + ratio; Point offsetRatioPoint = ConvertRatioOfRotationToPoint(offsetRatio, sliceRadius, sliceRadius); Point adjustedOffsetRatioPoint = offsetRatioPoint.Translate(translatePoint); // Calculate the last clockwise point in the pie slice Point currentRatioPoint = ConvertRatioOfRotationToPoint(currentRatio, sliceRadius, sliceRadius); // Adjust point using center of plot area as origin // instead of 0,0 Point adjustedCurrentRatioPoint = currentRatioPoint.Translate(translatePoint); foreach (DependencyProperty dependencyProperty in new DependencyProperty[] { PieDataPoint.GeometryProperty, PieDataPoint.GeometrySelectionProperty, PieDataPoint.GeometryHighlightProperty }) { // Creating the pie slice geometry object PathFigure pathFigure = new PathFigure { IsClosed = true }; pathFigure.StartPoint = translatePoint; pathFigure.Segments.Add(new LineSegment { Point = adjustedOffsetRatioPoint }); bool isLargeArc = (currentRatio - offsetRatio) > 0.5; pathFigure.Segments.Add( new ArcSegment { Point = adjustedCurrentRatioPoint, IsLargeArc = isLargeArc, Size = new Size(sliceRadius, sliceRadius), SweepDirection = SweepDirection.Clockwise }); PathGeometry pathGeometry = new PathGeometry(); pathGeometry.Figures.Add(pathFigure); pieDataPoint.SetValue(dependencyProperty, pathGeometry); } } } } /// /// Creates a legend item from a data point. /// /// The data point to use to create the legend item. /// The 1-based index of the Control. /// The series host legend item. protected virtual LegendItem CreatePieLegendItem(DataPoint dataPoint, int index) { LegendItem legendItem = CreateLegendItem(this); // Set the Content of the LegendItem legendItem.Content = dataPoint.IndependentValue ?? index; // Create a representative DataPoint for access to styled properties DataPoint legendDataPoint = CreateDataPoint(); legendDataPoint.DataContext = dataPoint.DataContext; if (null != PlotArea) { // Bounce into the visual tree to get default Style applied PlotArea.Children.Add(legendDataPoint); PlotArea.Children.Remove(legendDataPoint); } legendDataPoint.SetBinding(DataPoint.StyleProperty, new Binding(PieDataPoint.ActualDataPointStyleName) { Source = dataPoint }); legendItem.DataContext = legendDataPoint; return legendItem; } /// /// Attach event handlers to a data point. /// /// The data point. protected override void AttachEventHandlersToDataPoint(DataPoint dataPoint) { PieDataPoint pieDataPoint = dataPoint as PieDataPoint; pieDataPoint.ActualRatioChanged += OnPieDataPointActualRatioChanged; pieDataPoint.ActualOffsetRatioChanged += OnPieDataPointActualOffsetRatioChanged; pieDataPoint.RatioChanged += OnPieDataPointRatioChanged; pieDataPoint.OffsetRatioChanged += OnPieDataPointOffsetRatioChanged; base.AttachEventHandlersToDataPoint(dataPoint); } /// /// Detaches event handlers from a data point. /// /// The data point. protected override void DetachEventHandlersFromDataPoint(DataPoint dataPoint) { PieDataPoint pieDataPoint = dataPoint as PieDataPoint; pieDataPoint.ActualRatioChanged -= OnPieDataPointActualRatioChanged; pieDataPoint.ActualOffsetRatioChanged -= OnPieDataPointActualOffsetRatioChanged; pieDataPoint.RatioChanged -= OnPieDataPointRatioChanged; pieDataPoint.OffsetRatioChanged -= OnPieDataPointOffsetRatioChanged; base.DetachEventHandlersFromDataPoint(dataPoint); } /// /// This method updates the global series index property. /// /// The global index of the series. public void GlobalSeriesIndexChanged(int? globalIndex) { // Do nothing because we want to use up an index but do nothing // with it. } /// /// Updates the data point when the dependent value is changed. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue) { UpdateRatios(); base.OnDataPointDependentValueChanged(dataPoint, oldValue, newValue); } /// /// Updates the data point when the independent value is changed. /// /// The data point. /// The old value. /// The new value. protected override void OnDataPointIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { _dataPointLegendItems[dataPoint].Content = newValue; base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue); } /// /// Updates the data point when the pie data point's actual ratio is /// changed. /// /// The source of the event. /// Information about the event. private void OnPieDataPointActualRatioChanged(object sender, RoutedPropertyChangedEventArgs args) { UpdateDataPoint(sender as DataPoint); } /// /// Updates the data point when the pie data point's actual offset ratio /// is changed. /// /// The source of the event. /// Information about the event. private void OnPieDataPointActualOffsetRatioChanged(object sender, RoutedPropertyChangedEventArgs args) { UpdateDataPoint(sender as DataPoint); } /// /// Updates the data point when the pie data point's ratio is changed. /// /// The source of the event. /// Information about the event. private void OnPieDataPointRatioChanged(object sender, RoutedPropertyChangedEventArgs args) { DataPoint dataPoint = sender as DataPoint; dataPoint.BeginAnimation(PieDataPoint.ActualRatioProperty, "ActualRatio", args.NewValue, TransitionDuration, this.TransitionEasingFunction); } /// /// Updates the data point when the pie data point's offset ratio is /// changed. /// /// The source of the event. /// Information about the event. private void OnPieDataPointOffsetRatioChanged(object sender, RoutedPropertyChangedEventArgs args) { DataPoint dataPoint = sender as DataPoint; dataPoint.BeginAnimation(PieDataPoint.ActualOffsetRatioProperty, "ActualOffsetRatio", args.NewValue, TransitionDuration, this.TransitionEasingFunction); } /// /// Gets or sets an object used to dispense styles from the style /// palette. /// private ResourceDictionaryDispenser ResourceDictionaryDispenser { get; set; } /// /// Event that is invoked when the ResourceDictionaryDispenser's collection has changed. /// public event EventHandler ResourceDictionariesChanged; /// /// Returns a rotating enumerator of ResourceDictionary objects that coordinates /// with the dispenser object to ensure that no two enumerators are on the same /// item. If the dispenser is reset or its collection is changed then the /// enumerators are also reset. /// /// A predicate that returns a value indicating /// whether to return an item. /// An enumerator of ResourceDictionaries. public IEnumerator GetResourceDictionariesWhere(Func predicate) { return ResourceDictionaryDispenser.GetResourceDictionariesWhere(predicate); } /// /// Called when the value of the SeriesHost property changes. /// /// The value to be replaced. /// The new series host value. protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { base.OnSeriesHostPropertyChanged(oldValue, newValue); if (null != oldValue) { oldValue.ResourceDictionariesChanged -= new EventHandler(SeriesHostResourceDictionariesChanged); } if (null != newValue) { newValue.ResourceDictionariesChanged += new EventHandler(SeriesHostResourceDictionariesChanged); } else { // Dispose of the enumerator. if (null != _resourceDictionaryEnumerator) { _resourceDictionaryEnumerator.Dispose(); _resourceDictionaryEnumerator = null; } } this.ResourceDictionaryDispenser.Parent = newValue; } /// /// Handles the SeriesHost's ResourceDictionariesChanged event. /// /// ISeriesHost instance. /// Event args. private void SeriesHostResourceDictionariesChanged(object sender, EventArgs e) { Refresh(); } /// /// DataPointStyleProperty property changed handler. /// /// Old value. /// New value. protected override void OnDataPointStylePropertyChanged(Style oldValue, Style newValue) { // Propagate change foreach (PieDataPoint pieDataPoint in ActiveDataPoints) { pieDataPoint.ActualDataPointStyle = newValue ?? (pieDataPoint.Resources[DataPointStyleName] as Style); } base.OnDataPointStylePropertyChanged(oldValue, newValue); } /// /// Called when the value of the LegendItemStyle property changes. /// /// Old value. /// New value. protected override void OnLegendItemStylePropertyChanged(Style oldValue, Style newValue) { // Propagate change foreach (PieDataPoint pieDataPoint in ActiveDataPoints) { pieDataPoint.ActualLegendItemStyle = newValue ?? (pieDataPoint.Resources[LegendItemStyleName] as Style); } base.OnLegendItemStylePropertyChanged(oldValue, newValue); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/ScatterSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in X/Y scatter format. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ScatterDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] public partial class ScatterSeries : DataPointSingleSeriesWithAxes { /// /// Initializes a new instance of the ScatterSeries class. /// public ScatterSeries() { } /// /// Gets the dependent axis as a range axis. /// public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } #region public IRangeAxis DependentRangeAxis /// /// Gets or sets the dependent range axis. /// public IRangeAxis DependentRangeAxis { get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register( "DependentRangeAxis", typeof(IRangeAxis), typeof(ScatterSeries), new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); /// /// DependentRangeAxisProperty property changed handler. /// /// ScatterSeries that changed its DependentRangeAxis. /// Event arguments. private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ScatterSeries source = (ScatterSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnDependentRangeAxisPropertyChanged(newValue); } /// /// DependentRangeAxisProperty property changed handler. /// /// New value. private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; } #endregion public IRangeAxis DependentRangeAxis /// /// Gets the independent axis as a range axis. /// public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } #region public IAxis IndependentAxis /// /// Gets or sets the independent range axis. /// public IAxis IndependentAxis { get { return GetValue(IndependentAxisProperty) as IAxis; } set { SetValue(IndependentAxisProperty, value); } } /// /// Identifies the IndependentAxis dependency property. /// public static readonly DependencyProperty IndependentAxisProperty = DependencyProperty.Register( "IndependentAxis", typeof(IAxis), typeof(ScatterSeries), new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); /// /// IndependentAxisProperty property changed handler. /// /// ScatterSeries that changed its IndependentAxis. /// Event arguments. private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ScatterSeries source = (ScatterSeries)d; IAxis newValue = (IAxis)e.NewValue; source.OnIndependentAxisPropertyChanged(newValue); } /// /// IndependentAxisProperty property changed handler. /// /// New value. private void OnIndependentAxisPropertyChanged(IAxis newValue) { this.InternalIndependentAxis = (IAxis)newValue; } #endregion public IAxis IndependentAxis /// /// Acquire a horizontal linear axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// /// Creates a new scatter data point. /// /// A scatter data point. protected override DataPoint CreateDataPoint() { return new ScatterDataPoint(); } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// protected override IEnumerator GetResourceDictionaryEnumeratorFromHost() { return GetResourceDictionaryWithTargetType(SeriesHost, typeof(ScatterDataPoint), true); } /// /// This method updates a single data point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { double PlotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; double dataPointX = ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value; double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value; if (ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(dataPointY)) { dataPoint.Visibility = Visibility.Visible; // Set the Position Canvas.SetLeft( dataPoint, Math.Round(dataPointX - (dataPoint.ActualWidth / 2))); Canvas.SetTop( dataPoint, Math.Round(PlotAreaHeight - (dataPointY + (dataPoint.ActualHeight / 2)))); } else { dataPoint.Visibility = Visibility.Collapsed; } } } } #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Series.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.ObjectModel; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series. /// /// Preview public abstract partial class Series : Control, ISeries, IRequireSeriesHost { /// /// The name of the Title property. /// protected const string TitleName = "Title"; /// /// The name of the Visibility property. /// protected const string VisibilityName = "Visibility"; #region public ISeriesHost SeriesHost /// /// Gets or sets the parent instance the Series belongs to. /// public ISeriesHost SeriesHost { get { return _seriesHost; } set { ISeriesHost oldValue = _seriesHost; _seriesHost = value; if (oldValue != _seriesHost) { OnSeriesHostPropertyChanged(oldValue, _seriesHost); } } } /// /// Stores the Parent instance the Series belongs to. /// private ISeriesHost _seriesHost; /// /// Called when the value of the SeriesHost property changes. /// /// The value to be replaced. /// The new series host value. protected virtual void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) { if (newValue != null && oldValue != null) { throw new InvalidOperationException(Properties.Resources.Series_SeriesHost_SeriesHostPropertyNotNull); } } #endregion public ISeriesHost SeriesHost #region public ObservableCollection LegendItems /// /// Gets the legend items to be added to the legend. /// public ObservableCollection LegendItems { get; private set; } #endregion public ObservableCollection LegendItems #region public object Title /// /// Gets or sets the title content of the Series. /// public object Title { get { return GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( TitleName, typeof(object), typeof(Series), new PropertyMetadata(OnTitleChanged)); /// /// TitleProperty property changed callback. /// /// Series for which the Title changed. /// Event arguments. private static void OnTitleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((Series)o).OnTitleChanged(e.OldValue, e.NewValue); } /// /// Called when the Title property changes. /// /// The old value of the Title property. /// The new value of the Title property. protected virtual void OnTitleChanged(object oldValue, object newValue) { } #endregion public object Title /// /// Initializes a new instance of the Series class. /// protected Series() { LegendItems = new NoResetObservableCollection(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/SeriesDefinition.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Defines the attributes of a series that is to be rendered by the DefinitionSeries class. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(DataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = DataShapeStyleName, StyleTargetType = typeof(Shape))] public class SeriesDefinition : FrameworkElement, ISeries, IRequireGlobalSeriesIndex { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Name of the DataShapeStyle property. /// private const string DataShapeStyleName = "DataShapeStyle"; /// /// Provides the store for the ISeries.LegendItems property. /// private readonly ObservableCollection _legendItems = new ObservableCollection(); /// /// Represents the single LegendItem corresponding to the SeriesDefinition. /// private readonly LegendItem _legendItem; /// /// Keeps a reference to the WeakEventListener used to prevent leaks of collections assigned to the ItemsSource property. /// private WeakEventListener _weakEventListener; /// /// Gets or sets the index of the series definition. /// internal int Index { get; set; } /// /// Initializes a new instance of the SeriesDefinition class. /// public SeriesDefinition() { _legendItem = new LegendItem { Owner = this }; _legendItem.SetBinding(LegendItem.ContentProperty, new Binding("ActualTitle") { Source = this }); _legendItem.SetBinding(LegendItem.StyleProperty, new Binding("ActualLegendItemStyle") { Source = this }); _legendItems.Add(_legendItem); } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SeriesDefinition), new PropertyMetadata(OnItemsSourceChanged)); /// /// Handles changes to the ItemsSource dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnItemsSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue); } /// /// Handles changes to the ItemsSource property. /// /// Old value. /// New value. private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { // Remove handler for oldValue.CollectionChanged (if present) INotifyCollectionChanged oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged; if (null != oldValueINotifyCollectionChanged) { // Detach the WeakEventListener if (null != _weakEventListener) { _weakEventListener.Detach(); _weakEventListener = null; } } // Add handler for newValue.CollectionChanged (if possible) INotifyCollectionChanged newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged; if (null != newValueINotifyCollectionChanged) { // Use a WeakEventListener so that the backwards reference doesn't keep this object alive _weakEventListener = new WeakEventListener(this); _weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs); _weakEventListener.OnDetachAction = (weakEventListener) => newValueINotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent; newValueINotifyCollectionChanged.CollectionChanged += _weakEventListener.OnEvent; } if (null != ParentDefinitionSeries) { ParentDefinitionSeries.SeriesDefinitionItemsSourceChanged(this, oldValue, newValue); } } /// /// Handles the CollectionChanged event for the ItemsSource property. /// /// Event source. /// Event arguments.. private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (null != ParentDefinitionSeries) { ParentDefinitionSeries.SeriesDefinitionItemsSourceCollectionChanged(this, e.Action, e.OldItems, e.OldStartingIndex, e.NewItems, e.NewStartingIndex); } } /// /// Gets or sets the automatic title of the series definition. /// private object AutomaticTitle { get { return _automaticTitle; } set { _automaticTitle = value; ActualTitle = Title ?? _automaticTitle; } } /// /// Stores the automatic title of the series definition. /// private object _automaticTitle; /// /// Gets or sets the Title of the series definition. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(SeriesDefinition), new PropertyMetadata(OnTitleChanged)); /// /// Handles changes to the Title dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnTitleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnTitleChanged((object)e.OldValue, (object)e.NewValue); } /// /// Handles changes to the Title property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnTitleChanged(object oldValue, object newValue) { ActualTitle = newValue ?? _automaticTitle; } /// /// Gets the rendered Title of the series definition. /// public object ActualTitle { get { return (object)GetValue(ActualTitleProperty); } protected set { SetValue(ActualTitleProperty, value); } } /// /// Identifies the ActualTitle dependency property. /// public static readonly DependencyProperty ActualTitleProperty = DependencyProperty.Register("ActualTitle", typeof(object), typeof(SeriesDefinition), null); /// /// Gets or sets the DataPoint Style from the SeriesHost's Palette. /// internal Style PaletteDataPointStyle { get { return _paletteDataPointStyle; } set { _paletteDataPointStyle = value; ActualDataPointStyle = DataPointStyle ?? _paletteDataPointStyle; } } /// /// Stores the DataPoint Style from the SeriesHost's Palette. /// private Style _paletteDataPointStyle; /// /// Gets or sets the DataPoint Style for the series definition. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnDataPointStyleChanged)); /// /// Handles changes to the DataPointStyle dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnDataPointStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnDataPointStyleChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Handles changes to the DataPointStyle property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnDataPointStyleChanged(Style oldValue, Style newValue) { ActualDataPointStyle = newValue ?? _paletteDataPointStyle; } /// /// Gets the rendered DataPoint Style for the series definition. /// public Style ActualDataPointStyle { get { return (Style)GetValue(ActualDataPointStyleProperty); } protected set { SetValue(ActualDataPointStyleProperty, value); } } /// /// Identifies the ActualDataPointStyle dependency property. /// public static readonly DependencyProperty ActualDataPointStyleProperty = DependencyProperty.Register("ActualDataPointStyle", typeof(Style), typeof(SeriesDefinition), null); /// /// Gets or sets the LegendItem Style from the SeriesHost's Palette. /// internal Style PaletteLegendItemStyle { get { return _paletteLegendItemStyle; } set { _paletteLegendItemStyle = value; ActualLegendItemStyle = LegendItemStyle ?? _paletteLegendItemStyle; } } /// /// Stores the LegendItem Style from the SeriesHost's Palette. /// private Style _paletteLegendItemStyle; /// /// Gets or sets the LegendItem Style for the series definition. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnLegendItemStyleChanged)); /// /// Handles changes to the LegendItemStyle dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnLegendItemStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnLegendItemStyleChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Handles changes to the LegendItemStyle property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnLegendItemStyleChanged(Style oldValue, Style newValue) { ActualLegendItemStyle = newValue ?? _paletteLegendItemStyle; } /// /// Gets the rendered LegendItem Style for the series definition. /// public Style ActualLegendItemStyle { get { return (Style)GetValue(ActualLegendItemStyleProperty); } protected set { SetValue(ActualLegendItemStyleProperty, value); } } /// /// Identifies the ActualDataPointStyle dependency property. /// public static readonly DependencyProperty ActualLegendItemStyleProperty = DependencyProperty.Register("ActualLegendItemStyle", typeof(Style), typeof(SeriesDefinition), null); /// /// Gets or sets the DataShape Style from the SeriesHost's Palette. /// internal Style PaletteDataShapeStyle { get { return _paletteDataShapeStyle; } set { _paletteDataShapeStyle = value; ActualDataShapeStyle = DataShapeStyle ?? _paletteDataShapeStyle; } } /// /// Stores the DataShape Style from the SeriesHost's Palette. /// private Style _paletteDataShapeStyle; /// /// Gets or sets the DataShape Style for the series definition. /// public Style DataShapeStyle { get { return (Style)GetValue(DataShapeStyleProperty); } set { SetValue(DataShapeStyleProperty, value); } } /// /// Identifies the DataShapeStyle dependency property. /// public static readonly DependencyProperty DataShapeStyleProperty = DependencyProperty.Register(DataShapeStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnDataShapeStyleChanged)); /// /// Handles changes to the DataShapeStyle dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnDataShapeStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnDataShapeStyleChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Handles changes to the DataShapeStyle property. /// /// Old value. /// New value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] private void OnDataShapeStyleChanged(Style oldValue, Style newValue) { ActualDataShapeStyle = newValue ?? _paletteDataShapeStyle; } /// /// Gets the rendered DataShape Style for the series definition. /// public Style ActualDataShapeStyle { get { return (Style)GetValue(ActualDataShapeStyleProperty); } protected set { SetValue(ActualDataShapeStyleProperty, value); } } /// /// Identifies the ActualDataShapeStyle dependency property. /// public static readonly DependencyProperty ActualDataShapeStyleProperty = DependencyProperty.Register("ActualDataShapeStyle", typeof(Style), typeof(SeriesDefinition), null); /// /// Gets or sets the Binding to use for identifying the dependent value. /// public Binding DependentValueBinding { get { return _dependentValueBinding; } set { if (value != _dependentValueBinding) { _dependentValueBinding = value; Reset(); } } } /// /// The binding used to identify the dependent value binding. /// private Binding _dependentValueBinding; /// /// Gets or sets the Binding Path to use for identifying the dependent value. /// public string DependentValuePath { get { return (null != DependentValueBinding) ? DependentValueBinding.Path.Path : null; } set { if (null == value) { DependentValueBinding = null; } else { DependentValueBinding = new Binding(value); } } } /// /// Gets or sets the Binding to use for identifying the independent value. /// public Binding IndependentValueBinding { get { return _independentValueBinding; } set { if (_independentValueBinding != value) { _independentValueBinding = value; Reset(); } } } /// /// The binding used to identify the independent value binding. /// private Binding _independentValueBinding; /// /// Gets or sets the Binding Path to use for identifying the independent value. /// public string IndependentValuePath { get { return (null != IndependentValueBinding) ? IndependentValueBinding.Path.Path : null; } set { if (null == value) { IndependentValueBinding = null; } else { IndependentValueBinding = new Binding(value); } } } /// /// Resets the display of the series definition. /// private void Reset() { if (null != ParentDefinitionSeries) { ParentDefinitionSeries.SeriesDefinitionItemsSourceChanged(this, ItemsSource, ItemsSource); } } /// /// Gets the SeriesHost as a DefinitionSeries instance. /// private DefinitionSeries ParentDefinitionSeries { get { return (DefinitionSeries)((ISeries)this).SeriesHost; } } /// /// Gets the collection of legend items for the series definition. /// ObservableCollection ISeries.LegendItems { get { return _legendItems; } } /// /// Gets or sets the SeriesHost for the series definition. /// ISeriesHost IRequireSeriesHost.SeriesHost { get { return _seriesHost; } set { _seriesHost = value; if (!(_seriesHost is DefinitionSeries) && (null != value)) { throw new NotSupportedException(Properties.Resources.SeriesDefinition_SeriesHost_InvalidParent); } if (null != _seriesHost) { DataPoint legendItemDataPoint = ((DefinitionSeries)_seriesHost).InternalCreateDataPoint(); // Apply default style (easy) ContentControl contentControl = new ContentControl(); contentControl.Content = legendItemDataPoint; contentControl.Content = null; legendItemDataPoint.SetBinding(DataPoint.StyleProperty, new Binding("ActualDataPointStyle") { Source = this }); _legendItem.DataContext = legendItemDataPoint; } } } /// /// Stores the SeriesHost for the series definition. /// private ISeriesHost _seriesHost; /// /// Handles changes to the global series index of the series definition. /// /// New index. void IRequireGlobalSeriesIndex.GlobalSeriesIndexChanged(int? globalIndex) { if (globalIndex.HasValue) { AutomaticTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString, globalIndex + 1); } } /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(SeriesDefinition), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(SeriesDefinition), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/SeriesSelectionMode.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Defines the selection behavior for a series. /// public enum SeriesSelectionMode { /// /// Selection is disabled. /// None, /// /// The user can select only one item at a time. /// Single, /// /// The user can select multiple items without holding down a modifier key. /// Multiple, } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/SplineSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// /// Represents a control that contains a data series to be rendered in X/Y /// line format. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(LineDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = "PathStyle", StyleTargetType = typeof(Path))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] public partial class SplineSeries : LineAreaBaseSeries { #region public PointCollection Points /// /// Gets the collection of points that make up the spline. /// public PointCollection Points { get { return GetValue(PointsProperty) as PointCollection; } private set { SetValue(PointsProperty, value); } } /// /// Identifies the Points dependency property. /// public static readonly DependencyProperty PointsProperty = DependencyProperty.Register( "Points", typeof(PointCollection), typeof(SplineSeries), null); #endregion public PointCollection Points #region public PathGeometry SplinePoints /// /// Gets the collection of points that make up the line. /// public PathGeometry SplinePoints { get { return GetValue(SplinePointsProperty) as PathGeometry; } private set { SetValue(SplinePointsProperty, value); } } /// /// Identifies the SplinePoints dependency property. /// public static readonly DependencyProperty SplinePointsProperty = DependencyProperty.Register( "SplinePoints", typeof(PathGeometry), typeof(SplineSeries), null); #endregion public PathGeometry SplinePoints #region public double SplineTension /// /// Gets or sets the tension in the beziers that make up the spline. /// /// /// The greater the tension, the more straight/linear the spline will look. /// Less tension creates a more curvy spline. /// public double SplineTension { get { return (double)GetValue(SplineTensionProperty); } set { SetValue(SplineTensionProperty, value); } } /// /// Identifies the SplineTension dependency property. /// public static readonly DependencyProperty SplineTensionProperty = DependencyProperty.Register( "SplineTension", typeof(double), typeof(SplineSeries), new PropertyMetadata(2.5)); #endregion public double SplineTension #region public Style PathStyle /// /// Gets or sets the style of the Path object that follows the data /// points. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Path", Justification = "Matches System.Windows.Shapes.Path.")] public Style PathStyle { get { return GetValue(PathStyleProperty) as Style; } set { SetValue(PathStyleProperty, value); } } /// /// Identifies the PathStyle dependency property. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Path", Justification = "Matches System.Windows.Shapes.Path.")] public static readonly DependencyProperty PathStyleProperty = DependencyProperty.Register( "PathStyle", typeof(Style), typeof(SplineSeries), null); #endregion public Style PathStyle /// /// Initializes the static members of the LineSeries class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static SplineSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SplineSeries), new FrameworkPropertyMetadata(typeof(SplineSeries))); } /// /// Initializes a new instance of the LineSeries class. /// public SplineSeries() { } /// /// Acquire a horizontal linear axis and a vertical linear axis. /// /// The first data point. protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// /// Updates the Series shape object from a collection of Points. /// /// Collection of Points. protected override void UpdateShapeFromPoints(IEnumerable points) { if (points.Any()) { PointCollection pointCollection = new PointCollection(); foreach (Point point in points) { pointCollection.Add(point); } //At least two points are necessary to generate a proper spline if (pointCollection.Count >= 2) { PathGeometry geometry = new PathGeometry(); PathFigure figure = new PathFigure(); PointCollection bezierPoints = GetBezierPoints(pointCollection); figure.StartPoint = bezierPoints[0]; for (int i = 1; i < bezierPoints.Count; i += 3) { figure.Segments.Add(new BezierSegment() { Point1 = bezierPoints[i], Point2 = bezierPoints[i + 1], Point3 = bezierPoints[i + 2] }); } geometry.Figures.Add(figure); SplinePoints = geometry; } else { SplinePoints = null; } Points = pointCollection; } else { Points = null; SplinePoints = null; } } #region Bezier Curve Building /* * Formulas and code pulled from Kerem Kat's MapBezier example: * http://www.codeproject.com/KB/silverlight/MapBezier.aspx */ private PointCollection GetBezierPoints(PointCollection pts) { PointCollection ret = new PointCollection(); for (int i = 0; i < pts.Count; i++) { // for first point append as is. if (i == 0) { ret.Add(pts[0]); continue; } // for each point except first and last get B1, B2. next point. // Last point do not have a next point. ret.Add(GetB1(pts, i - 1, SplineTension)); ret.Add(GetB2(pts, i - 1, SplineTension)); ret.Add(pts[i]); } return ret; } private Point GetB1(PointCollection pts, int i, double a) { Point derivedPoint = GetDerivative(pts, i, a); return new Point(pts[i].X + derivedPoint.X / 3, pts[i].Y + derivedPoint.Y / 3); } private Point GetB2(PointCollection pts, int i, double a) { Point derivedPoint = GetDerivative(pts, i + 1, a); return new Point(pts[i + 1].X - derivedPoint.X / 3, pts[i + 1].Y - derivedPoint.Y / 3); } private Point GetDerivative(PointCollection pts, int i, double a) { if (pts.Count < 2) throw new ArgumentOutOfRangeException("pts", "Data must contain at least two points."); if (i == 0) { // First point. return new Point((pts[1].X - pts[0].X) / a, (pts[1].Y - pts[0].Y) / a); } if (i == pts.Count - 1) { // Last point. return new Point((pts[i].X - pts[i - 1].X) / a, (pts[i].Y - pts[i - 1].Y) / a); } return new Point((pts[i + 1].X - pts[i - 1].X) / a, (pts[i + 1].Y - pts[i - 1].Y) / a); } #endregion } } #endif ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Stacked100AreaSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a 100% stacked area chart visualization. /// /// Preview public class Stacked100AreaSeries : StackedAreaSeries { /// /// Initializes a new instance of the Stacked100AreaSeries class. /// public Stacked100AreaSeries() { IsStacked100 = true; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Stacked100BarSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a 100% stacked bar chart visualization. /// /// Preview public class Stacked100BarSeries : StackedBarSeries { /// /// Initializes a new instance of the Stacked100BarSeries class. /// public Stacked100BarSeries() { IsStacked100 = true; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Stacked100ColumnSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a 100% stacked column chart visualization. /// /// Preview public class Stacked100ColumnSeries : StackedColumnSeries { /// /// Initializes a new instance of the Stacked100ColumnSeries class. /// public Stacked100ColumnSeries() { IsStacked100 = true; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/Stacked100LineSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a 100% stacked line chart visualization. /// /// Preview public class Stacked100LineSeries : StackedLineSeries { /// /// Initializes a new instance of the Stacked100LineSeries class. /// public Stacked100LineSeries() { IsStacked100 = true; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/StackedAreaLineSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Data; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control base class for displaying values as a stacked area/line chart visualization. /// /// Preview public abstract class StackedAreaLineSeries : DefinitionSeries { /// /// Gets the Shapes corresponding to each SeriesDefinition. /// protected Dictionary SeriesDefinitionShapes { get; private set; } /// /// Initializes a new instance of the StackedAreaLineSeries class. /// protected StackedAreaLineSeries() { SeriesDefinitionShapes = new Dictionary(); } /// /// Builds the visual tree for the control when a new template is applied. /// public override void OnApplyTemplate() { SynchronizeSeriesDefinitionShapes(SeriesDefinitions, null); base.OnApplyTemplate(); SynchronizeSeriesDefinitionShapes(null, SeriesDefinitions); } /// /// Called when the SeriesDefinitions collection changes. /// /// Type of change. /// Sequence of old items. /// Starting index of old items. /// Sequence of new items. /// Starting index of new items. protected override void SeriesDefinitionsCollectionChanged(NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex, IList newItems, int newStartingIndex) { base.SeriesDefinitionsCollectionChanged(action, oldItems, oldStartingIndex, newItems, newStartingIndex); if (null != oldItems) { SynchronizeSeriesDefinitionShapes(oldItems.CastWrapper(), null); foreach (SeriesDefinition oldDefinition in oldItems.CastWrapper()) { SeriesDefinitionShapes.Remove(oldDefinition); } } if (null != newItems) { foreach (SeriesDefinition newDefinition in newItems.CastWrapper()) { Shape dataShape = CreateDataShape(); dataShape.SetBinding(Shape.StyleProperty, new Binding("ActualDataShapeStyle") { Source = newDefinition }); SeriesDefinitionShapes[newDefinition] = dataShape; } SynchronizeSeriesDefinitionShapes(null, newItems.CastWrapper()); } } /// /// Acquires a dependent axis suitable for use with the data values of the series. /// /// Axis instance. protected override IAxis AcquireDependentAxis() { IAxis dependentAxis = SeriesHost.Axes .Where(a => (a.Orientation == AxisOrientation.Y) && (a is IRangeAxis) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualDependentValue))) .FirstOrDefault(); if (null == dependentAxis) { LinearAxis linearAxis = new LinearAxis { Orientation = AxisOrientation.Y, ShowGridLines = true }; if (IsStacked100) { Style style = new Style(typeof(AxisLabel)); style.Setters.Add(new Setter(AxisLabel.StringFormatProperty, "{0}%")); linearAxis.AxisLabelStyle = style; } dependentAxis = linearAxis; } return dependentAxis; } /// /// Acquires an independent axis suitable for use with the data values of the series. /// /// Axis instance. protected override IAxis AcquireIndependentAxis() { IAxis independentAxis = SeriesHost.Axes .Where(a => (a.Orientation == AxisOrientation.X) && ((a is IRangeAxis) || (a is ICategoryAxis)) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualIndependentValue))) .FirstOrDefault(); if (null == independentAxis) { object probeValue = DataItems.Any() ? DataItems.First().ActualIndependentValue : null; double convertedDouble; DateTime convertedDateTime; if ((null != probeValue) && ValueHelper.TryConvert(probeValue, out convertedDouble)) { independentAxis = new LinearAxis(); } else if ((null != probeValue) && ValueHelper.TryConvert(probeValue, out convertedDateTime)) { independentAxis = new DateTimeAxis(); } else { independentAxis = new CategoryAxis(); } independentAxis.Orientation = AxisOrientation.X; } return independentAxis; } /// /// Prepares a DataPoint for use. /// /// DataPoint instance. protected override void PrepareDataPoint(DataPoint dataPoint) { base.PrepareDataPoint(dataPoint); dataPoint.SizeChanged += new SizeChangedEventHandler(DataPointSizeChanged); } /// /// Handles the SizeChanged event of a DataPoint to update the value margins for the series. /// /// Event source. /// Event arguments. private void DataPointSizeChanged(object sender, SizeChangedEventArgs e) { DataPoint dataPoint = (DataPoint)sender; DataItem dataItem = DataItemFromDataPoint(dataPoint); // Update placement double newWidth = e.NewSize.Width; double newHeight = e.NewSize.Height; Canvas.SetLeft(dataItem.Container, Math.Round(dataItem.CenterPoint.X - (newWidth / 2))); Canvas.SetTop(dataItem.Container, Math.Round(dataItem.CenterPoint.Y - (newHeight / 2))); // Update value margins double heightMargin = newHeight * (3.0 / 4.0); NotifyValueMarginsChanged(ActualDependentAxis, new ValueMargin[] { new ValueMargin(dataItem.ActualStackedDependentValue, heightMargin, heightMargin) }); double widthMargin = newWidth * (3.0 / 4.0); NotifyValueMarginsChanged(ActualIndependentAxis, new ValueMargin[] { new ValueMargin(dataPoint.ActualIndependentValue, widthMargin, widthMargin) }); } /// /// Creates a series-appropriate Shape for connecting the points of the series. /// /// Shape instance. protected abstract Shape CreateDataShape(); /// /// Synchronizes the SeriesDefinitionShapes dictionary with the contents of the SeriesArea Panel. /// /// SeriesDefinition being removed. /// SeriesDefinition being added. private void SynchronizeSeriesDefinitionShapes(IEnumerable oldDefinitions, IEnumerable newDefinitions) { if (null != SeriesArea) { if (null != oldDefinitions) { foreach (SeriesDefinition oldDefinition in oldDefinitions) { SeriesArea.Children.Remove(SeriesDefinitionShapes[oldDefinition]); } } if (null != newDefinitions) { foreach (SeriesDefinition newDefinition in newDefinitions.OrderBy(sd => sd.Index)) { SeriesArea.Children.Insert(newDefinition.Index, SeriesDefinitionShapes[newDefinition]); } } } } /// /// Returns the range for the data points of the series. /// /// Consumer of the range. /// Range of values. protected override Range IRangeProviderGetRange(IRangeConsumer rangeConsumer) { if (rangeConsumer == ActualDependentAxis) { IEnumerable> dependentValueRangesByIndependentValue = IndependentValueDependentValues .Select(g => g.Where(d => ValueHelper.CanGraph(d))) .Select(g => g.Scan(0.0, (s, t) => s + t).Skip(1).GetRange()) .DefaultIfEmpty(new Range(0, 0)) .ToArray(); double minimum = dependentValueRangesByIndependentValue.Min(r => r.Minimum); double maximum = dependentValueRangesByIndependentValue.Max(r => r.Maximum); if (IsStacked100) { minimum = Math.Min(minimum, 0); maximum = Math.Max(maximum, 0); } return new Range(minimum, maximum); } else { return base.IRangeProviderGetRange(rangeConsumer); } } /// /// Returns the value margins for the data points of the series. /// /// Consumer of the value margins. /// Sequence of value margins. protected override IEnumerable IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) { if (IsStacked100 && (valueMarginConsumer == ActualDependentAxis)) { return Enumerable.Empty(); } else if ((valueMarginConsumer == ActualDependentAxis) || (valueMarginConsumer == ActualIndependentAxis)) { Range range = IRangeProviderGetRange((IRangeConsumer)valueMarginConsumer); double margin = DataItems .Select(di => { return (null != di.DataPoint) ? (valueMarginConsumer == ActualDependentAxis) ? di.DataPoint.ActualHeight : di.DataPoint.ActualWidth : 0; }) .Average() * (3.0 / 4.0); return new ValueMargin[] { new ValueMargin(range.Minimum, margin, margin), new ValueMargin(range.Maximum, margin, margin), }; } else { return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); } } /// /// Updates the placement of the DataItems (data points) of the series. /// /// DataItems in need of an update. protected override void UpdateDataItemPlacement(IEnumerable dataItems) { if ((null != ActualDependentAxis) && (null != ActualIndependentAxis)) { double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; double lineTopBuffer = 1; List[] points = new List[SeriesDefinitions.Count]; for (int i = 0; i < points.Length; i++) { points[i] = new List(); } foreach (IndependentValueGroup group in IndependentValueGroupsOrderedByIndependentValue) { double sum = IsStacked100 ? group.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : 1; if (0 == sum) { sum = 1; } double x = ActualIndependentAxis.GetPlotAreaCoordinate(group.IndependentValue).Value; if (ValueHelper.CanGraph(x)) { double lastValue = 0; Point lastPoint = new Point(x, Math.Max(plotAreaMaximumDependentCoordinate - ActualDependentRangeAxis.GetPlotAreaCoordinate(lastValue).Value, lineTopBuffer)); int i = -1; SeriesDefinition lastDefinition = null; foreach (DataItem dataItem in group.DataItems) { if (lastDefinition != dataItem.SeriesDefinition) { i++; } while (dataItem.SeriesDefinition != SeriesDefinitions[i]) { points[i].Add(lastPoint); i++; } DataPoint dataPoint = dataItem.DataPoint; double value = IsStacked100 ? (ValueHelper.ToDouble(dataItem.DataPoint.ActualDependentValue) * (100 / sum)) : ValueHelper.ToDouble(dataItem.DataPoint.ActualDependentValue); if (ValueHelper.CanGraph(value)) { value += lastValue; dataItem.ActualStackedDependentValue = value; double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(value).Value; lastValue = value; lastPoint.Y = Math.Max(plotAreaMaximumDependentCoordinate - y, lineTopBuffer); points[i].Add(lastPoint); dataItem.CenterPoint = new Point(x, plotAreaMaximumDependentCoordinate - y); double left = dataItem.CenterPoint.X - (dataPoint.ActualWidth / 2); double top = dataItem.CenterPoint.Y - (dataPoint.ActualHeight / 2); Canvas.SetLeft(dataItem.Container, Math.Round(left)); Canvas.SetTop(dataItem.Container, Math.Round(top)); dataPoint.Visibility = Visibility.Visible; } else { points[i].Add(lastPoint); dataPoint.Visibility = Visibility.Collapsed; } lastDefinition = dataItem.SeriesDefinition; } } else { foreach (DataPoint dataPoint in group.DataItems.Select(di => di.DataPoint)) { dataPoint.Visibility = Visibility.Collapsed; } } } UpdateShape(points); } } /// /// Updates the Shape for the series. /// /// Locations of the points of each SeriesDefinition in the series. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nesting is convenient way to represent data.")] protected abstract void UpdateShape(IList> definitionPoints); } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/StackedAreaSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a stacked area chart visualization. /// /// Preview public class StackedAreaSeries : StackedAreaLineSeries, IAnchoredToOrigin { /// /// Initializes a new instance of the StackedAreaSeries class. /// public StackedAreaSeries() { } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected override DataPoint CreateDataPoint() { return new AreaDataPoint(); } /// /// Creates a series-appropriate Shape for connecting the points of the series. /// /// Shape instance. protected override Shape CreateDataShape() { return new Polygon(); } /// /// Updates the Shape for the series. /// /// Locations of the points of each SeriesDefinition in the series. protected override void UpdateShape(IList> definitionPoints) { for (int i = SeriesDefinitions.Count - 1; 0 < i; i--) { PointCollection pointCollection = new PointCollection(); IEnumerable topPoints = (ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i].OrderBy(p => p.X) : definitionPoints[i]; foreach (Point p in topPoints) { pointCollection.Add(p); } IEnumerable bottomPoints = (ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i - 1].OrderByDescending(p => p.X) : definitionPoints[i - 1].Reverse(); foreach (Point p in bottomPoints) { pointCollection.Add(p); } SetPolygonPointsProperty((Polygon)SeriesDefinitionShapes[SeriesDefinitions[i]], pointCollection); } if (1 <= SeriesDefinitions.Count) { double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; IComparable zeroValue = ActualDependentRangeAxis.Origin ?? 0.0; if (zeroValue.CompareTo(ActualDependentRangeAxis.Range.Minimum) < 0) { zeroValue = ActualDependentRangeAxis.Range.Minimum; } if (0 < zeroValue.CompareTo(ActualDependentRangeAxis.Range.Maximum)) { zeroValue = ActualDependentRangeAxis.Range.Maximum; } double zeroCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(zeroValue).Value; PointCollection pointCollection = new PointCollection(); Point[] topPoints = ((ActualIndependentAxis is ICategoryAxis) ? definitionPoints[0].OrderBy(p => p.X) : definitionPoints[0]).ToArray(); foreach (Point p in topPoints) { pointCollection.Add(p); } if (0 < topPoints.Length) { Point firstPoint = topPoints[0]; Point lastPoint = topPoints[topPoints.Length - 1]; pointCollection.Add(new Point(lastPoint.X, plotAreaMaximumDependentCoordinate - zeroCoordinate)); pointCollection.Add(new Point(firstPoint.X, plotAreaMaximumDependentCoordinate - zeroCoordinate)); } SetPolygonPointsProperty((Polygon)SeriesDefinitionShapes[SeriesDefinitions[0]], pointCollection); } } /// /// Sets the Points property of a Polygon to the specified PointCollection. /// /// Polygon to set the Points property of. /// Specified PointCollection. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Silverlight implementation is not static.")] protected void SetPolygonPointsProperty(Polygon polygon, PointCollection pointCollection) { polygon.Points = pointCollection; } /// /// Returns the value margins for the data points of the series. /// /// Consumer of the value margins. /// Sequence of value margins. protected override IEnumerable IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) { if (valueMarginConsumer == ActualIndependentAxis) { return Enumerable.Empty(); } else { return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); } } /// /// Gets the anchored axis for the series. /// IRangeAxis IAnchoredToOrigin.AnchoredAxis { get { return ActualDependentRangeAxis; } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/StackedBarColumnSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control base class for displaying values as a stacked bar/column chart visualization. /// /// Preview public abstract class StackedBarColumnSeries : DefinitionSeries, IAnchoredToOrigin { /// /// Gets or sets the orientation of the dependent axis. /// protected AxisOrientation DependentAxisOrientation { get; set; } /// /// Gets or sets the orientation of the independent axis. /// protected AxisOrientation IndependentAxisOrientation { get; set; } /// /// Initializes a new instance of the StackedBarColumnSeries class. /// protected StackedBarColumnSeries() { } /// /// Acquires a dependent axis suitable for use with the data values of the series. /// /// Axis instance. protected override IAxis AcquireDependentAxis() { IAxis dependentAxis = SeriesHost.Axes .Where(a => (a.Orientation == DependentAxisOrientation) && (a is IRangeAxis) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualDependentValue))) .FirstOrDefault(); if (null == dependentAxis) { LinearAxis linearAxis = new LinearAxis { Orientation = DependentAxisOrientation, ShowGridLines = true }; if (IsStacked100) { Style style = new Style(typeof(AxisLabel)); style.Setters.Add(new Setter(AxisLabel.StringFormatProperty, "{0}%")); linearAxis.AxisLabelStyle = style; } dependentAxis = linearAxis; } return dependentAxis; } /// /// Acquires an independent axis suitable for use with the data values of the series. /// /// Axis instance. protected override IAxis AcquireIndependentAxis() { IAxis independentAxis = SeriesHost.Axes .Where(a => (a.Orientation == IndependentAxisOrientation) && ((a is ICategoryAxis) || (a is IRangeAxis)) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualIndependentValue))) .FirstOrDefault(); if (null == independentAxis) { independentAxis = new CategoryAxis { Orientation = IndependentAxisOrientation }; } return independentAxis; } /// /// Returns the range for the data points of the series. /// /// Consumer of the range. /// Range of values. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq is artificially increasing the rating.")] protected override Range IRangeProviderGetRange(IRangeConsumer rangeConsumer) { if (rangeConsumer == ActualDependentAxis) { var dependentValuesByIndependentValue = IndependentValueDependentValues.Select(e => e.ToArray()).ToArray(); var mostNegative = dependentValuesByIndependentValue .Select(g => g.Where(v => v < 0) .Sum()) .Where(v => v < 0) .ToArray(); var leastNegative = dependentValuesByIndependentValue .Select(g => g.Where(v => v <= 0) .DefaultIfEmpty(1.0) .First()) .Where(v => v <= 0) .ToArray(); var mostPositive = dependentValuesByIndependentValue .Select(g => g.Where(v => 0 < v) .Sum()) .Where(v => 0 < v) .ToArray(); var leastPositive = dependentValuesByIndependentValue .Select(g => g.Where(v => 0 <= v) .DefaultIfEmpty(-1.0) .First()) .Where(v => 0 <= v) .ToArray(); // Compute minimum double minimum = 0; if (mostNegative.Any()) { minimum = mostNegative.Min(); } else if (leastPositive.Any()) { minimum = leastPositive.Min(); } // Compute maximum double maximum = 0; if (mostPositive.Any()) { maximum = mostPositive.Max(); } else if (leastNegative.Any()) { maximum = leastNegative.Max(); } if (IsStacked100) { minimum = Math.Min(minimum, 0); maximum = Math.Max(maximum, 0); } return new Range(minimum, maximum); } else if (rangeConsumer == ActualIndependentAxis) { // Using a non-ICategoryAxis for the independent axis // Need to specifically adjust for slot size of bars/columns so they don't overlap // Note: Calculation for slotSize is not perfect, but it's quick, close, and errs on the safe side Range range = base.IRangeProviderGetRange(rangeConsumer); int count = Math.Max(IndependentValueGroups.Count(), 1); if (ActualIndependentAxis.CanPlot(0.0)) { double minimum = ValueHelper.ToDouble(range.Minimum); double maximum = ValueHelper.ToDouble(range.Maximum); double slotSize = (maximum - minimum) / count; return new Range(minimum - slotSize, maximum + slotSize); } else { DateTime minimum = ValueHelper.ToDateTime(range.Minimum); DateTime maximum = ValueHelper.ToDateTime(range.Maximum); TimeSpan slotSize = TimeSpan.FromTicks((maximum - minimum).Ticks / count); return new Range(minimum - slotSize, maximum + slotSize); } } else { return base.IRangeProviderGetRange(rangeConsumer); } } /// /// Returns the value margins for the data points of the series. /// /// Consumer of the value margins. /// Sequence of value margins. protected override IEnumerable IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) { if (valueMarginConsumer == ActualDependentAxis) { if (IsStacked100) { return Enumerable.Empty(); } else { Range range = IRangeProviderGetRange((IRangeConsumer)ActualDependentAxis); double margin = ((AxisOrientation.Y == ActualDependentAxis.Orientation) ? ActualHeight : ActualWidth) / 10; return new ValueMargin[] { new ValueMargin(range.Minimum, margin, margin), new ValueMargin(range.Maximum, margin, margin), }; } } else if (valueMarginConsumer == ActualIndependentAxis) { // Using a non-ICategoryAxis for the independent axis // Relevant space already accounted for by IRangeProviderGetRange return Enumerable.Empty(); } else { return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); } } /// /// Updates the placement of the DataItems (data points) of the series. /// /// DataItems in need of an update. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq is artificially increasing the rating.")] protected override void UpdateDataItemPlacement(IEnumerable dataItems) { IAxis actualIndependentAxis = ActualIndependentAxis; if ((null != ActualDependentAxis) && (null != actualIndependentAxis)) { double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; double zeroCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin ?? 0.0).Value; ICategoryAxis actualIndependentCategoryAxis = actualIndependentAxis as ICategoryAxis; double nonCategoryAxisRangeMargin = (null != actualIndependentCategoryAxis) ? 0 : GetMarginForNonCategoryAxis(actualIndependentAxis); foreach (IndependentValueGroup group in IndependentValueGroups) { Range categoryRange = new Range(); if (null != actualIndependentCategoryAxis) { categoryRange = actualIndependentCategoryAxis.GetPlotAreaCoordinateRange(group.IndependentValue); } else { UnitValue independentValueCoordinate = actualIndependentAxis.GetPlotAreaCoordinate(group.IndependentValue); if (ValueHelper.CanGraph(independentValueCoordinate.Value)) { categoryRange = new Range(new UnitValue(independentValueCoordinate.Value - nonCategoryAxisRangeMargin, independentValueCoordinate.Unit), new UnitValue(independentValueCoordinate.Value + nonCategoryAxisRangeMargin, independentValueCoordinate.Unit)); } } if (categoryRange.HasData) { double categoryMinimumCoordinate = categoryRange.Minimum.Value; double categoryMaximumCoordinate = categoryRange.Maximum.Value; double padding = 0.1 * (categoryMaximumCoordinate - categoryMinimumCoordinate); categoryMinimumCoordinate += padding; categoryMaximumCoordinate -= padding; double sum = IsStacked100 ? group.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : 1; if (0 == sum) { sum = 1; } double ceiling = 0; double floor = 0; foreach (DataItem dataItem in group.DataItems) { DataPoint dataPoint = dataItem.DataPoint; double value = IsStacked100 ? (ValueHelper.ToDouble(dataPoint.ActualDependentValue) * (100 / sum)) : ValueHelper.ToDouble(dataPoint.ActualDependentValue); if (ValueHelper.CanGraph(value)) { double valueCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(value).Value; double fillerCoordinate = (0 <= value) ? ceiling : floor; double topCoordinate = 0, leftCoordinate = 0, height = 0, width = 0, deltaCoordinate = 0; if (AxisOrientation.Y == ActualDependentAxis.Orientation) { topCoordinate = plotAreaMaximumDependentCoordinate - Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); double bottomCoordinate = plotAreaMaximumDependentCoordinate - Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); deltaCoordinate = bottomCoordinate - topCoordinate; height = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0; leftCoordinate = categoryMinimumCoordinate; width = categoryMaximumCoordinate - categoryMinimumCoordinate + 1; } else { leftCoordinate = Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); double rightCoordinate = Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); deltaCoordinate = rightCoordinate - leftCoordinate; width = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0; topCoordinate = categoryMinimumCoordinate; height = categoryMaximumCoordinate - categoryMinimumCoordinate + 1; } double roundedTopCoordinate = Math.Round(topCoordinate); Canvas.SetTop(dataItem.Container, roundedTopCoordinate); dataPoint.Height = Math.Round(topCoordinate + height - roundedTopCoordinate); double roundedLeftCoordinate = Math.Round(leftCoordinate); Canvas.SetLeft(dataItem.Container, roundedLeftCoordinate); dataPoint.Width = Math.Round(leftCoordinate + width - roundedLeftCoordinate); dataPoint.Visibility = Visibility.Visible; if (0 <= value) { ceiling += deltaCoordinate; } else { floor -= deltaCoordinate; } } else { dataPoint.Visibility = Visibility.Collapsed; } } } else { foreach (DataPoint dataPoint in group.DataItems.Select(di => di.DataPoint)) { dataPoint.Visibility = Visibility.Collapsed; } } } } } /// /// Gets the margin to use for an independent axis that does not implement ICategoryAxis. /// /// Axis to get the margin for. /// Margin for axis. private double GetMarginForNonCategoryAxis(IAxis axis) { Debug.Assert(!(axis is ICategoryAxis), "This method is unnecessary for ICategoryAxis."); // Find the smallest distance between two independent value plot area coordinates double smallestDistance = double.MaxValue; double lastCoordinate = double.NaN; foreach (double coordinate in IndependentValueGroupsOrderedByIndependentValue .Select(g => axis.GetPlotAreaCoordinate(g.IndependentValue).Value) .Where(v => ValueHelper.CanGraph(v))) { if (!double.IsNaN(lastCoordinate)) { double distance = coordinate - lastCoordinate; if (distance < smallestDistance) { smallestDistance = distance; } } lastCoordinate = coordinate; } // Return the margin if (double.MaxValue == smallestDistance) { // No smallest distance because <= 1 independent values to plot FrameworkElement element = axis as FrameworkElement; if (null != element) { // Use width of provided axis so single column scenario looks good return element.GetMargin(axis); } else { // No information to work with; no idea what margin to return throw new NotSupportedException(); } } else { // Found the smallest distance; margin is half of that return smallestDistance / 2; } } /// /// Gets the anchored axis for the series. /// IRangeAxis IAnchoredToOrigin.AnchoredAxis { get { return ActualDependentRangeAxis; } } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/StackedBarSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a stacked bar chart visualization. /// /// Preview public class StackedBarSeries : StackedBarColumnSeries { /// /// Initializes a new instance of the StackedBarSeries class. /// public StackedBarSeries() { DependentAxisOrientation = AxisOrientation.X; IndependentAxisOrientation = AxisOrientation.Y; } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected override DataPoint CreateDataPoint() { return new BarDataPoint(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/StackedColumnSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a stacked column chart visualization. /// /// Preview public class StackedColumnSeries : StackedBarColumnSeries { /// /// Initializes a new instance of the StackedColumnSeries class. /// public StackedColumnSeries() { DependentAxisOrientation = AxisOrientation.Y; IndependentAxisOrientation = AxisOrientation.X; } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected override DataPoint CreateDataPoint() { return new ColumnDataPoint(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/Series/StackedLineSeries.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a stacked line chart visualization. /// /// Preview public class StackedLineSeries : StackedAreaLineSeries { /// /// Initializes a new instance of the StackedLineSeries class. /// public StackedLineSeries() { } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected override DataPoint CreateDataPoint() { return new LineDataPoint(); } /// /// Creates a series-appropriate Shape for connecting the points of the series. /// /// Shape instance. protected override Shape CreateDataShape() { return new Polyline { Fill = null }; } /// /// Updates the shape for the series. /// /// Locations of the points of each SeriesDefinition in the series. protected override void UpdateShape(IList> definitionPoints) { for (int i = 0; i < SeriesDefinitions.Count; i++) { PointCollection pointCollection = new PointCollection(); foreach (Point p in ((ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i].OrderBy(p => p.X) : definitionPoints[i])) { pointCollection.Add(p); } SetPolylinePointsProperty((Polyline)SeriesDefinitionShapes[SeriesDefinitions[i]], pointCollection); } } /// /// Sets the Points property of a Polyline to the specified PointCollection. /// /// Polyline to set the Points property of. /// Specified PointCollection. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches spelling of same-named framework class.")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "polyline", Justification = "Matches spelling of same-named framework class.")] [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Silverlight implementation is not static.")] protected void SetPolylinePointsProperty(Polyline polyline, PointCollection pointCollection) { polyline.Points = pointCollection; } } } ================================================ FILE: WpfToolkit/DataVisualization/Charting/ValueMarginCoordinateAndOverlap.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization.Charting { /// /// A class used to calculate axis range. /// internal class ValueMarginCoordinateAndOverlap { /// /// Gets or sets the value margin object. /// public ValueMargin ValueMargin { get; set; } /// /// Gets or sets the coordinate. /// public double Coordinate { get; set; } /// /// Gets or sets the left overlap. /// public double LeftOverlap { get; set; } /// /// Gets or sets the right overlap. /// public double RightOverlap { get; set; } /// /// Initializes a new instance of the ValueMarginCoordinateAndOverlap /// class. /// public ValueMarginCoordinateAndOverlap() { } } } ================================================ FILE: WpfToolkit/DataVisualization/Collections/LeftLeaningRedBlackTree.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. // Uncomment this to enable the following debugging aids: // LeftLeaningRedBlackTree.HtmlFragment // LeftLeaningRedBlackTree.Node.HtmlFragment // LeftLeaningRedBlackTree.AssertInvariants // #define DEBUGGING using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.DataVisualization.Collections { /// /// Implements a left-leaning red-black tree. /// /// /// Based on the research paper "Left-leaning Red-Black Trees" /// by Robert Sedgewick. More information available at: /// http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf /// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf /// /// Type of keys. /// Type of values. internal class LeftLeaningRedBlackTree { /// /// Stores the key comparison function. /// private Comparison _keyComparison; /// /// Stores the value comparison function. /// private Comparison _valueComparison; /// /// Stores the root node of the tree. /// private Node _rootNode; /// /// Represents a node of the tree. /// /// /// Using fields instead of properties drops execution time by about 40%. /// [DebuggerDisplay("Key={Key}, Value={Value}, Siblings={Siblings}")] private class Node { /// /// Gets or sets the node's key. /// public TKey Key; /// /// Gets or sets the node's value. /// public TValue Value; /// /// Gets or sets the left node. /// public Node Left; /// /// Gets or sets the right node. /// public Node Right; /// /// Gets or sets the color of the node. /// public bool IsBlack; /// /// Gets or sets the number of "siblings" (nodes with the same key/value). /// public int Siblings; #if DEBUGGING /// /// Gets an HTML fragment representing the node and its children. /// public string HtmlFragment { get { return "" + "" + "" + "" + "" + "" + "" + "" + "
" + Key + ", " + Value + " [" + Siblings + "]
" + (null != Left ? Left.HtmlFragment : "[null]") + "" + (null != Right ? Right.HtmlFragment : "[null]") + "
"; } } #endif } /// /// Initializes a new instance of the LeftLeaningRedBlackTree class implementing a normal dictionary. /// /// The key comparison function. public LeftLeaningRedBlackTree(Comparison keyComparison) { if (null == keyComparison) { throw new ArgumentNullException("keyComparison"); } _keyComparison = keyComparison; } /// /// Initializes a new instance of the LeftLeaningRedBlackTree class implementing an ordered multi-dictionary. /// /// The key comparison function. /// The value comparison function. public LeftLeaningRedBlackTree(Comparison keyComparison, Comparison valueComparison) : this(keyComparison) { if (null == valueComparison) { throw new ArgumentNullException("valueComparison"); } _valueComparison = valueComparison; } /// /// Gets a value indicating whether the tree is acting as an ordered multi-dictionary. /// private bool IsMultiDictionary { get { return null != _valueComparison; } } /// /// Adds a key/value pair to the tree. /// /// Key to add. /// Value to add. public void Add(TKey key, TValue value) { _rootNode = Add(_rootNode, key, value); _rootNode.IsBlack = true; #if DEBUGGING AssertInvariants(); #endif } /// /// Removes a key (and its associated value) from a normal (non-multi) dictionary. /// /// Key to remove. /// True if key present and removed. public bool Remove(TKey key) { if (IsMultiDictionary) { throw new InvalidOperationException("Remove is only supported when acting as a normal (non-multi) dictionary."); } return Remove(key, default(TValue)); } /// /// Removes a key/value pair from the tree. /// /// Key to remove. /// Value to remove. /// True if key/value present and removed. public bool Remove(TKey key, TValue value) { int initialCount = Count; if (null != _rootNode) { _rootNode = Remove(_rootNode, key, value); if (null != _rootNode) { _rootNode.IsBlack = true; } } #if DEBUGGING AssertInvariants(); #endif return initialCount != Count; } /// /// Removes all nodes in the tree. /// public void Clear() { _rootNode = null; Count = 0; #if DEBUGGING AssertInvariants(); #endif } /// /// Gets a sorted list of keys in the tree. /// /// Sorted list of keys. public IEnumerable GetKeys() { TKey lastKey = default(TKey); bool lastKeyValid = false; return Traverse( _rootNode, n => !lastKeyValid || !object.Equals(lastKey, n.Key), n => { lastKey = n.Key; lastKeyValid = true; return lastKey; }); } /// /// Gets the value associated with the specified key in a normal (non-multi) dictionary. /// /// Specified key. /// Value associated with the specified key. [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "GetValueForKey", Justification = "Method name.")] public TValue GetValueForKey(TKey key) { if (IsMultiDictionary) { throw new InvalidOperationException("GetValueForKey is only supported when acting as a normal (non-multi) dictionary."); } Node node = GetNodeForKey(key); if (null != node) { return node.Value; } else { throw new KeyNotFoundException(); } } /// /// Gets a sequence of the values associated with the specified key. /// /// Specified key. /// Sequence of values. public IEnumerable GetValuesForKey(TKey key) { return Traverse(GetNodeForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value); } /// /// Gets a sequence of all the values in the tree. /// /// Sequence of all values. public IEnumerable GetValuesForAllKeys() { return Traverse(_rootNode, n => true, n => n.Value); } /// /// Gets the count of key/value pairs in the tree. /// public int Count { get; private set; } /// /// Gets the minimum key in the tree. /// public TKey MinimumKey { get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); } } /// /// Gets the maximum key in the tree. /// public TKey MaximumKey { get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); } } /// /// Gets the minimum key's minimum value. /// public TValue MinimumValue { get { return GetExtreme(_rootNode, n => n.Left, n => n.Value); } } /// /// Gets the maximum key's maximum value. /// public TValue MaximumValue { get { return GetExtreme(_rootNode, n => n.Right, n => n.Value); } } /// /// Returns true if the specified node is red. /// /// Specified node. /// True if specified node is red. private static bool IsRed(Node node) { if (null == node) { // "Virtual" leaf nodes are always black return false; } return !node.IsBlack; } /// /// Adds the specified key/value pair below the specified root node. /// /// Specified node. /// Key to add. /// Value to add. /// New root node. private Node Add(Node node, TKey key, TValue value) { if (null == node) { // Insert new node Count++; return new Node { Key = key, Value = value }; } if (IsRed(node.Left) && IsRed(node.Right)) { // Split node with two red children FlipColor(node); } // Find right place for new node int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); if (comparisonResult < 0) { node.Left = Add(node.Left, key, value); } else if (0 < comparisonResult) { node.Right = Add(node.Right, key, value); } else { if (IsMultiDictionary) { // Store the presence of a "duplicate" node node.Siblings++; Count++; } else { // Replace the value of the existing node node.Value = value; } } if (IsRed(node.Right)) { // Rotate to prevent red node on right node = RotateLeft(node); } if (IsRed(node.Left) && IsRed(node.Left.Left)) { // Rotate to prevent consecutive red nodes node = RotateRight(node); } return node; } /// /// Removes the specified key/value pair from below the specified node. /// /// Specified node. /// Key to remove. /// Value to remove. /// True if key/value present and removed. private Node Remove(Node node, TKey key, TValue value) { int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); if (comparisonResult < 0) { // * Continue search if left is present if (null != node.Left) { if (!IsRed(node.Left) && !IsRed(node.Left.Left)) { // Move a red node over node = MoveRedLeft(node); } // Remove from left node.Left = Remove(node.Left, key, value); } } else { if (IsRed(node.Left)) { // Flip a 3 node or unbalance a 4 node node = RotateRight(node); } if ((0 == KeyAndValueComparison(key, value, node.Key, node.Value)) && (null == node.Right)) { // Remove leaf node Debug.Assert(null == node.Left, "About to remove an extra node."); Count--; if (0 < node.Siblings) { // Record the removal of the "duplicate" node Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); node.Siblings--; return node; } else { // Leaf node is gone return null; } } // * Continue search if right is present if (null != node.Right) { if (!IsRed(node.Right) && !IsRed(node.Right.Left)) { // Move a red node over node = MoveRedRight(node); } if (0 == KeyAndValueComparison(key, value, node.Key, node.Value)) { // Remove leaf node Count--; if (0 < node.Siblings) { // Record the removal of the "duplicate" node Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); node.Siblings--; } else { // Find the smallest node on the right, swap, and remove it Node m = GetExtreme(node.Right, n => n.Left, n => n); node.Key = m.Key; node.Value = m.Value; node.Siblings = m.Siblings; node.Right = DeleteMinimum(node.Right); } } else { // Remove from right node.Right = Remove(node.Right, key, value); } } } // Maintain invariants return FixUp(node); } /// /// Flip the colors of the specified node and its direct children. /// /// Specified node. private static void FlipColor(Node node) { node.IsBlack = !node.IsBlack; node.Left.IsBlack = !node.Left.IsBlack; node.Right.IsBlack = !node.Right.IsBlack; } /// /// Rotate the specified node "left". /// /// Specified node. /// New root node. private static Node RotateLeft(Node node) { Node x = node.Right; node.Right = x.Left; x.Left = node; x.IsBlack = node.IsBlack; node.IsBlack = false; return x; } /// /// Rotate the specified node "right". /// /// Specified node. /// New root node. private static Node RotateRight(Node node) { Node x = node.Left; node.Left = x.Right; x.Right = node; x.IsBlack = node.IsBlack; node.IsBlack = false; return x; } /// /// Moves a red node from the right child to the left child. /// /// Parent node. /// New root node. private static Node MoveRedLeft(Node node) { FlipColor(node); if (IsRed(node.Right.Left)) { node.Right = RotateRight(node.Right); node = RotateLeft(node); FlipColor(node); // * Avoid creating right-leaning nodes if (IsRed(node.Right.Right)) { node.Right = RotateLeft(node.Right); } } return node; } /// /// Moves a red node from the left child to the right child. /// /// Parent node. /// New root node. private static Node MoveRedRight(Node node) { FlipColor(node); if (IsRed(node.Left.Left)) { node = RotateRight(node); FlipColor(node); } return node; } /// /// Deletes the minimum node under the specified node. /// /// Specified node. /// New root node. private Node DeleteMinimum(Node node) { if (null == node.Left) { // Nothing to do return null; } if (!IsRed(node.Left) && !IsRed(node.Left.Left)) { // Move red node left node = MoveRedLeft(node); } // Recursively delete node.Left = DeleteMinimum(node.Left); // Maintain invariants return FixUp(node); } /// /// Maintains invariants by adjusting the specified nodes children. /// /// Specified node. /// New root node. private static Node FixUp(Node node) { if (IsRed(node.Right)) { // Avoid right-leaning node node = RotateLeft(node); } if (IsRed(node.Left) && IsRed(node.Left.Left)) { // Balance 4-node node = RotateRight(node); } if (IsRed(node.Left) && IsRed(node.Right)) { // Push red up FlipColor(node); } // * Avoid leaving behind right-leaning nodes if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left)) { node.Left = RotateLeft(node.Left); if (IsRed(node.Left)) { // Balance 4-node node = RotateRight(node); } } return node; } /// /// Gets the (first) node corresponding to the specified key. /// /// Key to search for. /// Corresponding node or null if none found. private Node GetNodeForKey(TKey key) { // Initialize Node node = _rootNode; while (null != node) { // Compare keys and go left/right int comparisonResult = _keyComparison(key, node.Key); if (comparisonResult < 0) { node = node.Left; } else if (0 < comparisonResult) { node = node.Right; } else { // Match; return node return node; } } // No match found return null; } /// /// Gets an extreme (ex: minimum/maximum) value. /// /// Type of value. /// Node to start from. /// Successor function. /// Selector function. /// Extreme value. private static T GetExtreme(Node node, Func successor, Func selector) { // Initialize T extreme = default(T); Node current = node; while (null != current) { // Go to extreme extreme = selector(current); current = successor(current); } return extreme; } /// /// Traverses a subset of the sequence of nodes in order and selects the specified nodes. /// /// Type of elements. /// Starting node. /// Condition method. /// Selector method. /// Sequence of selected nodes. private IEnumerable Traverse(Node node, Func condition, Func selector) { // Create a stack to avoid recursion Stack stack = new Stack(); Node current = node; while (null != current) { if (null != current.Left) { // Save current state and go left stack.Push(current); current = current.Left; } else { do { for (int i = 0; i <= current.Siblings; i++) { // Select current node if relevant if (condition(current)) { yield return selector(current); } } // Go right - or up if nothing to the right current = current.Right; } while ((null == current) && (0 < stack.Count) && (null != (current = stack.Pop()))); } } } /// /// Compares the specified keys (primary) and values (secondary). /// /// The left key. /// The left value. /// The right key. /// The right value. /// CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right. private int KeyAndValueComparison(TKey leftKey, TValue leftValue, TKey rightKey, TValue rightValue) { // Compare keys int comparisonResult = _keyComparison(leftKey, rightKey); if ((0 == comparisonResult) && (null != _valueComparison)) { // Keys match; compare values comparisonResult = _valueComparison(leftValue, rightValue); } return comparisonResult; } #if DEBUGGING /// /// Asserts that tree invariants are not violated. /// private void AssertInvariants() { // Root is black Debug.Assert((null == _rootNode) || _rootNode.IsBlack, "Root is not black"); // Every path contains the same number of black nodes Dictionary parents = new Dictionary.Node, LeftLeaningRedBlackTree.Node>(); foreach (Node node in Traverse(_rootNode, n => true, n => n)) { if (null != node.Left) { parents[node.Left] = node; } if (null != node.Right) { parents[node.Right] = node; } } if (null != _rootNode) { parents[_rootNode] = null; } int treeCount = -1; foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n)) { int pathCount = 0; Node current = node; while (null != current) { if (current.IsBlack) { pathCount++; } current = parents[current]; } Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes."); treeCount = pathCount; } // Verify node properties... foreach (Node node in Traverse(_rootNode, n => true, n => n)) { // Left node is less if (null != node.Left) { Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent."); } // Right node is greater if (null != node.Right) { Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent."); } // Both children of a red node are black Debug.Assert(!IsRed(node) || (!IsRed(node.Left) && !IsRed(node.Right)), "Red node has a red child."); // Always left-leaning Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning."); // No consecutive reds (subset of previous rule) //Debug.Assert(!(IsRed(node) && IsRed(node.Left))); } } /// /// Gets an HTML fragment representing the tree. /// public string HtmlDocument { get { return "" + "" + (null != _rootNode ? _rootNode.HtmlFragment : "[null]") + "" + ""; } } #endif } } ================================================ FILE: WpfToolkit/DataVisualization/Collections/MultipleDictionary.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace System.Windows.Controls.DataVisualization.Collections { /// /// Implements a dictionary that can store multiple values for the same key. /// /// Type for keys. /// Type for values. internal class MultipleDictionary { /// /// Gets or sets the BinaryTree instance used to store the dictionary values. /// protected LeftLeaningRedBlackTree BinaryTree { get; set; } /// /// Initializes a new instance of the MultipleDictionary class. /// protected MultipleDictionary() { } /// /// Initializes a new instance of the MultipleDictionary class. /// /// The parameter is not used. /// The parameter is not used. /// The parameter is not used. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "allowDuplicateValues", Justification = "Unused parameter exists for API compatibility.")] public MultipleDictionary(bool allowDuplicateValues, IEqualityComparer keyEqualityComparer, IEqualityComparer valueEqualityComparer) { Debug.Assert(null != keyEqualityComparer, "keyEqualityComparer must not be null."); Debug.Assert(null != valueEqualityComparer, "valueEqualityComparer must not be null."); BinaryTree = new LeftLeaningRedBlackTree( (left, right) => keyEqualityComparer.GetHashCode(left).CompareTo(keyEqualityComparer.GetHashCode(right)), (left, right) => valueEqualityComparer.GetHashCode(left).CompareTo(valueEqualityComparer.GetHashCode(right))); } /// /// Adds a key/value pair to the dictionary. /// /// Key to add. /// Value to add. public void Add(TKey key, TValue value) { BinaryTree.Add(key, value); } /// /// Removes a key/value pair from the dictionary. /// /// Key to remove. /// Value to remove. /// True if the value was present and removed. public bool Remove(TKey key, TValue value) { return BinaryTree.Remove(key, value); } /// /// Gets the count of values in the dictionary. /// public int Count { get { return BinaryTree.Count; } } /// /// Returns the collection of values corresponding to a key. /// /// Specified key. /// Collection of values. public ICollection this[TKey key] { get { return BinaryTree.GetValuesForKey(key).ToList(); } } /// /// Clears the items in the dictionary. /// public void Clear() { BinaryTree.Clear(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Collections/OrderedMultipleDictionary.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace System.Windows.Controls.DataVisualization.Collections { /// /// Implements a dictionary that can store multiple values for the same key and sorts the values. /// /// Type for keys. /// Type for values. internal class OrderedMultipleDictionary : MultipleDictionary, IEnumerable where TKey : IComparable { /// /// Initializes a new instance of the MultipleDictionary class. /// /// The parameter is not used. /// Key comparison class. /// Value comparison class. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "allowDuplicateValues", Justification = "Unused parameter exists for API compatibility.")] public OrderedMultipleDictionary(bool allowDuplicateValues, Comparison keyComparison, Comparison valueComparison) { Debug.Assert(null != keyComparison, "keyComparison must not be null."); Debug.Assert(null != valueComparison, "valueComparison must not be null."); BinaryTree = new LeftLeaningRedBlackTree(keyComparison, valueComparison); } /// /// Gets a Range corresponding to the keys in the dictionary. /// /// Range of keys. public Range GetKeyRange() { if (0 < BinaryTree.Count) { return new Range(BinaryTree.MinimumKey, BinaryTree.MaximumKey); } else { return new Range(); } } /// /// Gets the largest and smallest key's extreme values from the dictionary. /// /// Tuple of the largest and smallest values. public Tuple GetLargestAndSmallestValues() { if (0 < BinaryTree.Count) { return new Tuple(BinaryTree.MinimumValue, BinaryTree.MaximumValue); } else { return null; } } /// /// Gets an enumerator for the values in the dictionary. /// /// Enumerator for values. public IEnumerator GetEnumerator() { return BinaryTree.GetValuesForAllKeys().GetEnumerator(); } /// /// Gets an enumerator for the values in the dictionary. /// /// Enumerator for the values. IEnumerator IEnumerable.GetEnumerator() { return BinaryTree.GetValuesForAllKeys().GetEnumerator(); } } } ================================================ FILE: WpfToolkit/DataVisualization/ColorExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Windows.Media; namespace System.Windows.Controls { /// /// Contains extensions to Color structure for HSL color mode. /// internal static class ColorExtensions { /// /// Gets the color component (R,G or B) with maximal value. /// /// The red component value for the Color. Valid values are 0 through 255. /// The green component value for the Color. Valid values are 0 through 255. /// The blue component value for the Color. Valid values are 0 through 255. /// Component with maximal value. private static byte MaxColor(byte red, byte green, byte blue) { return Math.Max(red, Math.Max(green, blue)); } /// /// Gets the color component (R,G or B) with minimal value. /// /// The red component value for the Color. Valid values are 0 through 255. /// The green component value for the Color. Valid values are 0 through 255. /// The blue component value for the Color. Valid values are 0 through 255. /// Component with minimal value. private static byte MinColor(byte red, byte green, byte blue) { return Math.Min(red, Math.Min(green, blue)); } /// /// The hue, in degrees, of the Color. /// The hue is measured in degrees, ranging from 0.0 through 360.0, /// in HSL color space. /// /// The Color structure that this method uses. /// /// The hue, in degrees, of the Color structure. The hue is /// measured in degrees, ranging from 0.0 through 360.0, in HSL color /// space. /// public static float GetHue(this Color color) { float hue; int max = MaxColor(color.R, color.G, color.B); int min = MinColor(color.R, color.G, color.B); int denominator = max - min; if (denominator == 0) { return 0f; } if (color.R == max) { hue = (float)(color.G - color.B) / denominator; } else if (color.G == max) { hue = 2f + ((float)(color.B - color.R) / denominator); } else { // if (color.B == max) hue = 4f + ((float)(color.R - color.G) / denominator); } hue *= 60f; if (hue < 0f) { hue += 360f; } return hue; } /// /// Gets the hue-saturation-brightness (HSL ) saturation value for the /// Color structure. /// /// The Color structure that this method uses. /// /// The saturation of the Color structure. The saturation ranges from /// 0.0 through 1.0, where 0.0 is grayscale and 1.0 is the most saturate. /// public static float GetSaturation(this Color color) { float max = MaxColor(color.R, color.G, color.B) / 255f; float min = MinColor(color.R, color.G, color.B) / 255f; if (max - min == 0) { return 0f; } float median = (max + min) / 2f; if (median <= 0.5) { return (float)(max - min) / (max + min); } return (max - min) / ((2f - max) - min); } /// /// Gets the hue-saturation-lightness (HSL) lightness value for the /// Color structure. /// /// The Color structure that this method uses. /// /// The brightness of the Color. The brightness ranges from 0.0 through /// 1.0, where 0.0 represents black and 1.0 represents white. /// public static float GetLightness(this Color color) { float max = MaxColor(color.R, color.G, color.B) / 255f; float min = MinColor(color.R, color.G, color.B) / 255f; return (max + min) / 2f; } /// /// Creates a new color from HSL parameters. /// /// The Color structure that this method uses. /// The alpha component. Valid values are 0 through 255. /// The hue component. Valid values are [0, 360). /// The saturation component. Valid values are [0, 1]. /// The brightness component. Valid values are [0, 1]. /// A newly created color structure. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Following FromArgb convention.")] public static Color FromAhsl(this Color color, byte a, double h, double s, double l) { double[] t = new double[3]; // normalize hue h = h / 360; double q = l < 0.5 ? l * (1 + s) : l + s - (l * s); double p = (2 * l) - q; t[0] = h + (1.0 / 3.0); t[1] = h; t[2] = h - (1.0 / 3.0); for (int i = 0; i < 3; i++) { // t(c) if (t[i] < 0) { t[i] += 1.0; } else if (t[i] > 1) { t[i] -= 1.0; } // Calculate Color(c) if (t[i] * 6.0 < 1.0) { t[i] = p + ((q - p) * 6 * t[i]); } else if (t[i] * 2.0 < 1.0) { t[i] = q; } else if (t[i] * 3.0 < 2.0) { t[i] = p + ((q - p) * 6 * ((2.0 / 3.0) - t[i])); } else { t[i] = p; } } color.A = a; // Denormalize RGB color.R = (byte)Math.Round(t[0] * 255); color.G = (byte)Math.Round(t[1] * 255); color.B = (byte)Math.Round(t[2] * 255); return color; } } } ================================================ FILE: WpfToolkit/DataVisualization/DependencyPropertyAnimationHelper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Media.Animation; namespace System.Windows.Controls.DataVisualization { /// /// Represents a control that can animate the transitions between its specified /// dependency property. /// internal static class DependencyPropertyAnimationHelper { /// /// Number of key frames per second to generate the date time animations. /// public const int KeyFramesPerSecond = 20; /// /// The pattern used to ensure unique keys for the storyboards stored in /// a framework element's resource dictionary. /// private const string StoryboardKeyPattern = "__{0}__"; /// /// Returns a unique key for a storyboard. /// /// The property path of the property that /// the storyboard animates. /// A unique key for a storyboard. private static string GetStoryboardKey(string propertyPath) { return string.Format(CultureInfo.InvariantCulture, StoryboardKeyPattern, propertyPath); } /// /// Starts animating a dependency property of a framework element to a /// target value. /// /// The element to animate. /// The dependency property to /// animate. /// The path of the dependency property to /// animate. /// The value to animate the dependency /// property to. /// The duration of the animation. /// The easing function to uses to /// transition the data points. public static void BeginAnimation( this FrameworkElement target, DependencyProperty animatingDependencyProperty, string propertyPath, object targetValue, TimeSpan timeSpan, IEasingFunction easingFunction) { Storyboard storyBoard = target.Resources[GetStoryboardKey(propertyPath)] as Storyboard; if (storyBoard != null) { storyBoard.Stop(); target.Resources.Remove(GetStoryboardKey(propertyPath)); } storyBoard = CreateStoryboard(target, animatingDependencyProperty, propertyPath, ref targetValue, timeSpan, easingFunction); storyBoard.Completed += (source, args) => { storyBoard.Stop(); target.SetValue(animatingDependencyProperty, targetValue); target.Resources.Remove(GetStoryboardKey(propertyPath)); }; target.Resources.Add(GetStoryboardKey(propertyPath), storyBoard); storyBoard.Begin(); } /// /// Creates a story board that animates a dependency property to a /// value. /// /// The element that is the target of the /// storyboard. /// The dependency property /// to animate. /// The property path of the dependency /// property to animate. /// The value to animate the dependency property /// to. /// The duration of the animation. /// /// The easing function to use to /// transition the data points. /// The story board that animates the property. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "easingFunction", Justification = "This parameter is used in Silverlight.")] private static Storyboard CreateStoryboard( FrameworkElement target, DependencyProperty animatingDependencyProperty, string propertyPath, ref object toValue, TimeSpan durationTimeSpan, IEasingFunction easingFunction) { object fromValue = target.GetValue(animatingDependencyProperty); double fromDoubleValue; double toDoubleValue; DateTime fromDateTime; DateTime toDateTime; Storyboard storyBoard = new Storyboard(); Storyboard.SetTarget(storyBoard, target); Storyboard.SetTargetProperty(storyBoard, new PropertyPath(propertyPath)); if ((fromValue != null && toValue != null)) { if (ValueHelper.TryConvert(fromValue, out fromDoubleValue) && ValueHelper.TryConvert(toValue, out toDoubleValue)) { DoubleAnimation doubleAnimation = new DoubleAnimation(); #if !NO_EASING_FUNCTIONS doubleAnimation.EasingFunction = easingFunction; #endif doubleAnimation.Duration = durationTimeSpan; doubleAnimation.To = ValueHelper.ToDouble(toValue); toValue = doubleAnimation.To; storyBoard.Children.Add(doubleAnimation); } else if (ValueHelper.TryConvert(fromValue, out fromDateTime) && ValueHelper.TryConvert(toValue, out toDateTime)) { ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames(); keyFrameAnimation.Duration = durationTimeSpan; long intervals = (long)(durationTimeSpan.TotalSeconds * KeyFramesPerSecond); if (intervals < 2L) { intervals = 2L; } IEnumerable timeSpanIntervals = ValueHelper.GetTimeSpanIntervalsInclusive(durationTimeSpan, intervals); IEnumerable dateTimeIntervals = ValueHelper.GetDateTimesBetweenInclusive(fromDateTime, toDateTime, intervals); IEnumerable keyFrames = EnumerableFunctions.Zip( dateTimeIntervals, timeSpanIntervals, (dateTime, timeSpan) => new DiscreteObjectKeyFrame() { Value = dateTime, KeyTime = timeSpan }); foreach (DiscreteObjectKeyFrame keyFrame in keyFrames) { keyFrameAnimation.KeyFrames.Add(keyFrame); toValue = keyFrame.Value; } storyBoard.Children.Add(keyFrameAnimation); } } if (storyBoard.Children.Count == 0) { ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames(); DiscreteObjectKeyFrame endFrame = new DiscreteObjectKeyFrame() { Value = toValue, KeyTime = new TimeSpan(0, 0, 0) }; keyFrameAnimation.KeyFrames.Add(endFrame); storyBoard.Children.Add(keyFrameAnimation); } return storyBoard; } } } ================================================ FILE: WpfToolkit/DataVisualization/DesignerProperties.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls { /// /// Provides a custom implementation of DesignerProperties.GetIsInDesignMode /// to work around an issue. /// internal static class DesignerProperties { /// /// Returns whether the control is in design mode (running under Blend /// or Visual Studio). /// /// The element from which the property value is /// read. /// True if in design mode. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "element", Justification = "Matching declaration of System.ComponentModel.DesignerProperties.GetIsInDesignMode (which has a bug and is not reliable).")] public static bool GetIsInDesignMode(DependencyObject element) { if (!_isInDesignMode.HasValue) { _isInDesignMode = System.ComponentModel.DesignerProperties.GetIsInDesignMode(element); } return _isInDesignMode.Value; } /// /// Stores the computed InDesignMode value. /// private static bool? _isInDesignMode; } } ================================================ FILE: WpfToolkit/DataVisualization/DotNetProjects.DataVisualization.Toolkit.csproj ================================================  System.Windows.Controls.DataVisualization net5.0-windows;net6.0-windows;net4 true true DotNetProjects.snk WPF Toolkit Data Visualization Controls true DotNetProjects.WpfToolkit.DataVisualization DotNetProjects DotNetProjects DotNetProjects.WPF Toolkit 2021 DotNetProjects 1.0.0 1.0.0.0 1.0.0.0 MS-PL ================================================ FILE: WpfToolkit/DataVisualization/EnumerableExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace System.Windows.Controls { /// /// A set of extension methods for the sequence class. /// internal static class EnumerableExtensions { /// /// Produces a sequence of items using a seed value and iteration /// method. /// /// The type of the sequence. /// The initial value. /// The iteration function. /// A sequence of items using a seed value and iteration /// method. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by at least one consumer of this class.")] public static IEnumerable Iterate(T value, Func next) { do { yield return value; value = next(value); } while (true); } /// /// Prepend an item to a sequence. /// /// The type of the sequence. /// The sequence to append the item to. /// The item to append to the sequence. /// A new sequence. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Code is linked into multiple projects.")] public static IEnumerable Prepend(this IEnumerable that, T value) { if (that == null) { throw new ArgumentNullException("that"); } yield return value; foreach (T item in that) { yield return item; } } /// /// Accepts two sequences and applies a function to the corresponding /// values in the two sequences. /// /// The type of the first sequence. /// The type of the second sequence. /// The return type of the function. /// The first sequence. /// The second sequence. /// The function to apply to the corresponding values /// from the two sequences. /// A sequence of transformed values from both sequences. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Code is linked into multiple projects.")] public static IEnumerable Zip(IEnumerable enumerable0, IEnumerable enumerable1, Func func) { IEnumerator enumerator0 = enumerable0.GetEnumerator(); IEnumerator enumerator1 = enumerable1.GetEnumerator(); while (enumerator0.MoveNext() && enumerator1.MoveNext()) { yield return func(enumerator0.Current, enumerator1.Current); } } /// /// Returns the maximum value in the stream based on the result of a /// project function. /// /// The stream type. /// The stream. /// The function that transforms the /// item. /// The maximum value or null. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by at least one consumer of this class.")] public static T MaxOrNull(this IEnumerable that, Func projectionFunction) where T : struct { IComparable result = null; T maximum = default(T); if (!that.Any()) { return maximum; } maximum = that.First(); result = projectionFunction(maximum); foreach (T item in that.Skip(1)) { IComparable currentResult = projectionFunction(item); if (result.CompareTo(currentResult) < 0) { result = currentResult; maximum = item; } } return maximum; } /// /// Returns the maximum value or null if sequence is empty. /// /// The type of the sequence. /// The sequence to retrieve the maximum value from. /// /// The maximum value or null. public static T? MaxOrNullable(this IEnumerable that) where T : struct, IComparable { if (!that.Any()) { return null; } return that.Max(); } /// /// Returns the minimum value or null if sequence is empty. /// /// The type of the sequence. /// The sequence to retrieve the minimum value from. /// /// The minimum value or null. public static T? MinOrNullable(this IEnumerable that) where T : struct, IComparable { if (!that.Any()) { return null; } return that.Min(); } } } ================================================ FILE: WpfToolkit/DataVisualization/EnumerableFunctions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; namespace System.Windows.Controls.DataVisualization { /// /// This class contains general purpose functions to manipulate the generic /// IEnumerable type. /// internal static class EnumerableFunctions { /// /// Attempts to cast IEnumerable to a list in order to retrieve a count /// in order one. It attempts to cast fail the sequence is enumerated. /// /// The sequence. /// The number of elements in the sequence. public static int FastCount(this IEnumerable that) { IList list = that as IList; if (list != null) { return list.Count; } return that.CastWrapper().Count(); } /// /// Returns the minimum value in the stream based on the result of a /// project function. /// /// The stream type. /// The stream. /// The function that transforms the /// item. /// The minimum value or null. public static T MinOrNull(this IEnumerable that, Func projectionFunction) where T : class { IComparable result = null; T minimum = default(T); if (!that.Any()) { return minimum; } minimum = that.First(); result = projectionFunction(minimum); foreach (T item in that.Skip(1)) { IComparable currentResult = projectionFunction(item); if (result.CompareTo(currentResult) > 0) { result = currentResult; minimum = item; } } return minimum; } /// /// Returns the sum of all values in the sequence or the default value. /// /// The stream. /// The sum of all values or the default value. public static double SumOrDefault(this IEnumerable that) { if (!that.Any()) { return 0.0; } else { return that.Sum(); } } /// /// Returns the maximum value in the stream based on the result of a /// project function. /// /// The stream type. /// The stream. /// The function that transforms the /// item. /// The maximum value or null. public static T MaxOrNull(this IEnumerable that, Func projectionFunction) where T : class { IComparable result = null; T maximum = default(T); if (!that.Any()) { return maximum; } maximum = that.First(); result = projectionFunction(maximum); foreach (T item in that.Skip(1)) { IComparable currentResult = projectionFunction(item); if (result.CompareTo(currentResult) < 0) { result = currentResult; maximum = item; } } return maximum; } /// /// Accepts two sequences and applies a function to the corresponding /// values in the two sequences. /// /// The type of the first sequence. /// The type of the second sequence. /// The return type of the function. /// The first sequence. /// The second sequence. /// The function to apply to the corresponding values /// from the two sequences. /// A sequence of transformed values from both sequences. public static IEnumerable Zip(IEnumerable enumerable0, IEnumerable enumerable1, Func func) { IEnumerator enumerator0 = enumerable0.GetEnumerator(); IEnumerator enumerator1 = enumerable1.GetEnumerator(); while (enumerator0.MoveNext() && enumerator1.MoveNext()) { yield return func(enumerator0.Current, enumerator1.Current); } } /// /// Creates a sequence of values by accepting an initial value, an /// iteration function, and apply the iteration function recursively. /// /// The type of the sequence. /// The initial value. /// The function to apply to the value. /// /// A sequence of the iterated values. public static IEnumerable Iterate(T value, Func nextFunction) { yield return value; while (true) { value = nextFunction(value); yield return value; } } /// /// Returns the index of an item in a sequence. /// /// The sequence. /// The item to search for. /// The index of the item or -1 if not found. public static int IndexOf(this IEnumerable that, object value) { int index = 0; foreach (object item in that) { if (object.ReferenceEquals(value, item) || value.Equals(item)) { return index; } index++; } return -1; } /// /// Executes an action for each item and a sequence, passing in the /// index of that item to the action procedure. /// /// The type of the sequence. /// The sequence. /// A function that accepts a sequence item and its /// index in the sequence. public static void ForEachWithIndex(this IEnumerable that, Action action) { int index = 0; foreach (T item in that) { action(item, index); index++; } } /// /// Returns the maximum value or null if sequence is empty. /// /// The type of the sequence. /// The sequence to retrieve the maximum value from. /// /// The maximum value or null. public static T? MaxOrNullable(this IEnumerable that) where T : struct, IComparable { if (!that.Any()) { return null; } return that.Max(); } /// /// Returns the minimum value or null if sequence is empty. /// /// The type of the sequence. /// The sequence to retrieve the minimum value from. /// /// The minimum value or null. public static T? MinOrNullable(this IEnumerable that) where T : struct, IComparable { if (!that.Any()) { return null; } return that.Min(); } /// /// Attempts to retrieve an element at an index by testing whether a /// sequence is randomly accessible. If not, performance degrades to a /// linear search. /// /// The type of the elements in the sequence. /// The sequence. /// The index of the element in the sequence. /// The element at the given index. public static T FastElementAt(this IEnumerable that, int index) { { IList list = that as IList; if (list != null) { return list[index]; } } { IList list = that as IList; if (list != null) { return (T) list[index]; } } return that.CastWrapper().ElementAt(index); } /// /// Applies an accumulator function over a sequence and returns each intermediate result. /// /// Type of elements in source sequence. /// Type of elements in result sequence. /// Sequence to scan. /// Initial accumulator value. /// Function used to generate the result sequence. /// Sequence of intermediate results. public static IEnumerable Scan(this IEnumerable that, S seed, Func accumulator) { S value = seed; yield return seed; foreach (T t in that) { value = accumulator(value, t); yield return value; } yield break; } /// /// Converts the elements of an System.Collections.IEnumerable to the specified type. /// /// /// A wrapper for the Enumerable.Cast(T) method that works around a limitation on some platforms. /// /// The type to convert the elements of source to. /// The System.Collections.IEnumerable that contains the elements to be converted. /// /// An System.Collections.Generic.IEnumerable(T) that contains each element of the source sequence converted to the specified type. /// public static IEnumerable CastWrapper(this IEnumerable source) { // No issues on this platform - call directly through to Cast return source.Cast(); } } } ================================================ FILE: WpfToolkit/DataVisualization/GenericEqualityComparer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization { /// /// A generic equality comparer. /// /// The type of the objects being compared. internal class GenericEqualityComparer : EqualityComparer { /// /// Gets or sets a function which determines whether two items are equal. /// public Func EqualityFunction { get; set; } /// /// Gets or sets a function that returns a hash code for an object. /// public Func HashCodeFunction { get; set; } /// /// Initializes a new instance of the GenericEqualityComparer class. /// /// A function which determines whether /// two items are equal. /// A function that returns a hash code /// for an object. public GenericEqualityComparer(Func equalityFunction, Func hashCodeFunction) { this.EqualityFunction = equalityFunction; this.HashCodeFunction = hashCodeFunction; } /// /// A function which determines whether two items are equal. /// /// The left object. /// The right object. /// A value indicating whether the objects. are equal. public override bool Equals(T x, T y) { return EqualityFunction(x, y); } /// /// A function that returns a hash code for an object. /// /// The object to returns a hash code for. /// The hash code for the object. public override int GetHashCode(T obj) { return HashCodeFunction(obj); } } } ================================================ FILE: WpfToolkit/DataVisualization/GlobalSuppressions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Unofficial release.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Headered", Justification = "Part of HeaderedItemsControl.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches System.Windows.Shapes.Polyline.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Intelli", Justification = "Part of IntelliSense.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Silverlight", Justification = "Product name.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "tuple's", Justification = "Consistent with MSDN documentation for .NET 4.")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.ActualMaximum")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.ActualMinimum")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.GetPlotAreaCoordinate(System.IComparable)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.RangeChanged(System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider,System.Windows.Controls.DataVisualization.Range`1)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.GetPlotAreaCoordinateValueRange(System.Double)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.ICategoryAxisInformationProvider.GetCategories(System.Windows.Controls.DataVisualization.Charting.ICategoryAxis)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider.GetActualRange(System.Windows.Controls.DataVisualization.Charting.IRangeAxis)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider.GetDesiredRange(System.Windows.Controls.DataVisualization.Charting.IRangeAxis)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.Axis.#System.Windows.Controls.DataVisualization.Charting.IAxis.Register(System.Object)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.Axis.#System.Windows.Controls.DataVisualization.Charting.IAxis.Unregister(System.Object)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.Range")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IAxisListener.AxisInvalidated(System.Windows.Controls.DataVisualization.Charting.IAxis)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IDataProvider.GetData(System.Windows.Controls.DataVisualization.Charting.IDataConsumer)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IRangeProvider.GetRange(System.Windows.Controls.DataVisualization.Charting.IRangeConsumer)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.CollectionBase`1.#System.Collections.ICollection.SyncRoot")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.CollectionBase`1.#System.Collections.Generic.ICollection`1.IsReadOnly")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.ListBaseCollection`1.#System.Collections.IList.IsFixedSize")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.ListBaseCollection`1.#System.Collections.IList.IsReadOnly")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.CollectionBase`1.#System.Collections.ICollection.IsSynchronized")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.ColumnBarBaseSeries`1.#System.Windows.Controls.DataVisualization.Charting.IAnchoredToOrigin.AnchoredAxis")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeConsumer.RangeChanged(System.Windows.Controls.DataVisualization.Charting.IRangeProvider,System.Windows.Controls.DataVisualization.Range`1)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IValueMarginConsumer.ValueMarginsChanged(System.Windows.Controls.DataVisualization.Charting.IValueMarginProvider,System.Collections.Generic.IEnumerable`1)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IAxisListener.AxisInvalidated(System.Windows.Controls.DataVisualization.Charting.IAxis)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IRangeProvider.GetRange(System.Windows.Controls.DataVisualization.Charting.IRangeConsumer)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.IResourceDictionaryDispenser.GetResourceDictionariesWhere(System.Func`2)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.IResourceDictionaryDispenser.ResourceDictionariesChanged")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.IResourceDictionaryDispenser.ResourceDictionariesChanged")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.Axes")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.BackgroundElements")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.ForegroundElements")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.Series")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IValueMarginProvider.GetValueMargins(System.Windows.Controls.DataVisualization.Charting.IValueMarginConsumer)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.IRequireGlobalSeriesIndex.GlobalSeriesIndexChanged(System.Nullable`1)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.IRequireSeriesHost.SeriesHost")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.IRequireSeriesHost.SeriesHost")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.ISeries.LegendItems")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.StackedAreaSeries.#System.Windows.Controls.DataVisualization.Charting.IAnchoredToOrigin.AnchoredAxis")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.StackedBarColumnSeries.#System.Windows.Controls.DataVisualization.Charting.IAnchoredToOrigin.AnchoredAxis")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.StackedBarColumnSeries.#System.Windows.Controls.DataVisualization.Charting.IDataProvider.GetData(System.Windows.Controls.DataVisualization.Charting.IDataConsumer)")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IDataProvider.GetData(System.Windows.Controls.DataVisualization.Charting.IDataConsumer)")] [assembly: SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.PieSeries.#.cctor()")] [assembly: SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesHostAxesCollection.#.ctor(System.Windows.Controls.DataVisualization.Charting.ISeriesHost)")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "XamlGeneratedNamespace")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Controls.DataVisualization.Charting.Primitives")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Controls.DataVisualization.Charting.Compatible")] // Suppress "Silverlight-only class or assembly" warning for all DataVisualization classes because there is currently no corresponding WPF implementation. [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AnimationSequence")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Axis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AxisLabel")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AxisLocation")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AxisOrientation")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BarDataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BarSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BubbleDataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BubbleSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.CategoryAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.CategorySortOrder")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Chart")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ColumnDataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ColumnSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointSingleSeriesWithAxes")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointState")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DateTimeAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DateTimeAxisLabel")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DateTimeIntervalType")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAxisGridLinesElementProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ICategoryAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ICategoryAxisInformationProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRequireGlobalSeriesIndex")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ISeriesHost")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LegendItem")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LineDataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LineSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LinearAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.NumericAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.NumericAxisLabel")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.PieDataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.PieSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.RangeAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ScatterDataPoint")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ScatterSeries")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Series")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.IStyleDispenser")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Legend")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Range`1")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.StringFormatConverter")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.ResourceDictionaryCollection")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Title")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ColumnBarBaseSeries`1")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DisplayAxis")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAnnotationProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAxisListener")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IDataConsumer")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IDataProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeConsumer")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRequireSeriesHost")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IScrollService")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IValueMarginConsumer")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IValueMarginProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.PointOrientedPanel")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.RadialPanel")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ValueMargin")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.IScrollServiceInformationProvider")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Unit")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.UnitValue")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.ListBaseCollection`1")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.CollectionBase`1")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Primitives.Edge")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Primitives.EdgePanel")] ================================================ FILE: WpfToolkit/DataVisualization/GridExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; namespace System.Windows.Controls.DataVisualization { /// /// A set of extension methods for the Grid container. /// internal static class GridExtensions { /// /// Mirrors the grid either horizontally or vertically. /// /// The grid to mirror. /// The orientation to mirror the grid along. /// public static void Mirror(this Grid grid, Orientation orientation) { if (orientation == Orientation.Horizontal) { IList rows = grid.RowDefinitions.Reverse().ToList(); grid.RowDefinitions.Clear(); foreach (FrameworkElement child in grid.Children.OfType()) { Grid.SetRow(child, (rows.Count - 1) - Grid.GetRow(child)); } foreach (RowDefinition row in rows) { grid.RowDefinitions.Add(row); } } else if (orientation == Orientation.Vertical) { IList columns = grid.ColumnDefinitions.Reverse().ToList(); grid.ColumnDefinitions.Clear(); foreach (FrameworkElement child in grid.Children.OfType()) { Grid.SetColumn(child, (columns.Count - 1) - Grid.GetColumn(child)); } foreach (ColumnDefinition column in columns) { grid.ColumnDefinitions.Add(column); } } } } } ================================================ FILE: WpfToolkit/DataVisualization/IResourceDictionaryDispenser.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization { /// /// Represents a service that dispenses ResourceDictionaries. /// /// Preview public interface IResourceDictionaryDispenser { /// /// Returns a rotating enumerator of ResourceDictionaries coordinated with /// the style dispenser object to ensure that no two enumerators are /// currently on the same one if possible. If the dispenser is reset or /// its collection is changed then the enumerators will also be reset. /// /// A predicate that returns a value /// indicating whether to return a ResourceDictionary. /// An enumerator of ResourceDictionaries. IEnumerator GetResourceDictionariesWhere(Func predicate); /// /// Event that is invoked when the StyleDispenser's ResourceDictionaries have changed. /// event EventHandler ResourceDictionariesChanged; } } ================================================ FILE: WpfToolkit/DataVisualization/LayoutTransformControl.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; using System.Windows.Media; using System.IO; using System.Text; namespace System.Windows.Controls.DataVisualization { /// /// Control that implements support for transformations as if applied by /// LayoutTransform (which does not exist in Silverlight). /// [ContentProperty("Child")] internal class LayoutTransformControl : Control { #region public FrameworkElement Child /// /// Gets or sets the single child of the LayoutTransformControl. /// /// /// Corresponds to Windows Presentation Foundation's Decorator.Child /// property. /// public FrameworkElement Child { get { return (FrameworkElement)GetValue(ContentProperty); } set { SetValue(ContentProperty, value); } } /// /// Identifies the ContentProperty. /// public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( "Child", typeof(FrameworkElement), typeof(LayoutTransformControl), new PropertyMetadata(ChildChanged)); #endregion public FrameworkElement Child #region public Transform Transform /// /// Gets or sets the Transform of the LayoutTransformControl. /// /// /// Corresponds to UIElement.RenderTransform. /// public Transform Transform { get { return (Transform)GetValue(TransformProperty); } set { SetValue(TransformProperty, value); } } /// /// Identifies the TransformProperty dependency property. /// public static readonly DependencyProperty TransformProperty = DependencyProperty.Register( "Transform", typeof(Transform), typeof(LayoutTransformControl), new PropertyMetadata(TransformChanged)); #endregion /// /// Value used to work around double arithmetic rounding issues in /// Silverlight. /// private const double AcceptableDelta = 0.0001; /// /// Value used to work around double arithmetic rounding issues in /// Silverlight. /// private const int DecimalsAfterRound = 4; /// /// Host panel for Child element. /// private Panel _layoutRoot; /// /// RenderTransform/MatrixTransform applied to layout root. /// private MatrixTransform _matrixTransform; /// /// Transformation matrix corresponding to matrix transform. /// private Matrix _transformation; /// /// Actual DesiredSize of Child element. /// private Size _childActualSize = Size.Empty; /// /// Initializes a new instance of the LayoutTransformControl class. /// public LayoutTransformControl() { // Can't tab to LayoutTransformControl IsTabStop = false; // Hard coded template is never meant to be changed and avoids the // need for generic.xaml. string templateXaml = @"" + "" + "" + "" + "" + "" + ""; using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(templateXaml))) { Template = (ControlTemplate)XamlReader.Load(stream); } } /// /// Called whenever the control's template changes. /// public override void OnApplyTemplate() { // Save existing content and remove it from the visual tree FrameworkElement savedContent = Child; Child = null; // Apply new template base.OnApplyTemplate(); // Find template parts _layoutRoot = GetTemplateChild("LayoutRoot") as Panel; _matrixTransform = GetTemplateChild("MatrixTransform") as MatrixTransform; // Restore saved content Child = savedContent; // Apply the current transform TransformUpdated(); } /// /// Handle changes to the child dependency property. /// /// The source of the event. /// Information about the event. private static void ChildChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { // Casts are safe because Silverlight is enforcing the types ((LayoutTransformControl)o).OnChildChanged((FrameworkElement)e.NewValue); } /// /// Updates content when the child property is changed. /// /// The new child. private void OnChildChanged(FrameworkElement newContent) { if (null != _layoutRoot) { // Clear current child _layoutRoot.Children.Clear(); if (null != newContent) { // Add the new child to the tree _layoutRoot.Children.Add(newContent); } // New child means re-layout is necessary InvalidateMeasure(); } } /// /// Handles changes to the Transform DependencyProperty. /// /// The source of the event. /// Information about the event. private static void TransformChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { // Casts are safe because Silverlight is enforcing the types ((LayoutTransformControl)o).OnTransformChanged((Transform)e.NewValue); } /// /// Processes the transform when the transform is changed. /// /// The transform to process. private void OnTransformChanged(Transform newValue) { ProcessTransform(newValue); } /// /// Notifies the LayoutTransformControl that some aspect of its /// Transform property has changed. /// /// /// Call this to update the LayoutTransform in cases where /// LayoutTransformControl wouldn't otherwise know to do so. /// public void TransformUpdated() { ProcessTransform(Transform); } /// /// Processes the current transform to determine the corresponding /// matrix. /// /// The transform to use to determine the /// matrix. private void ProcessTransform(Transform transform) { // Get the transform matrix and apply it _transformation = RoundMatrix(GetTransformMatrix(transform), DecimalsAfterRound); if (null != _matrixTransform) { _matrixTransform.Matrix = _transformation; } // New transform means re-layout is necessary InvalidateMeasure(); } /// /// Walks the Transform and returns the corresponding matrix. /// /// The transform to create a matrix for. /// /// The matrix calculated from the transform. private Matrix GetTransformMatrix(Transform transform) { if (null != transform) { // WPF equivalent of this entire method: // return transform.Value; // Process the TransformGroup TransformGroup transformGroup = transform as TransformGroup; if (null != transformGroup) { Matrix groupMatrix = Matrix.Identity; foreach (Transform child in transformGroup.Children) { groupMatrix = MatrixMultiply(groupMatrix, GetTransformMatrix(child)); } return groupMatrix; } // Process the RotateTransform RotateTransform rotateTransform = transform as RotateTransform; if (null != rotateTransform) { double angle = rotateTransform.Angle; double angleRadians = (2 * Math.PI * angle) / 360; double sine = Math.Sin(angleRadians); double cosine = Math.Cos(angleRadians); return new Matrix(cosine, sine, -sine, cosine, 0, 0); } // Process the ScaleTransform ScaleTransform scaleTransform = transform as ScaleTransform; if (null != scaleTransform) { double scaleX = scaleTransform.ScaleX; double scaleY = scaleTransform.ScaleY; return new Matrix(scaleX, 0, 0, scaleY, 0, 0); } // Process the SkewTransform SkewTransform skewTransform = transform as SkewTransform; if (null != skewTransform) { double angleX = skewTransform.AngleX; double angleY = skewTransform.AngleY; double angleXRadians = (2 * Math.PI * angleX) / 360; double angleYRadians = (2 * Math.PI * angleY) / 360; return new Matrix(1, angleYRadians, angleXRadians, 1, 0, 0); } // Process the MatrixTransform MatrixTransform matrixTransform = transform as MatrixTransform; if (null != matrixTransform) { return matrixTransform.Matrix; } // TranslateTransform has no effect in LayoutTransform } // Fall back to no-op transformation return Matrix.Identity; } /// /// Provides the behavior for the "Measure" pass of layout. /// /// The available size that this element can /// give to child elements. Infinity can be specified as a value to /// indicate that the element will size to whatever content is /// available. /// The size that this element determines it needs during /// layout, based on its calculations of child element sizes. protected override Size MeasureOverride(Size availableSize) { FrameworkElement child = Child; if ((null == _layoutRoot) || (null == child)) { // No content, no size return Size.Empty; } Size measureSize; if (_childActualSize == Size.Empty) { // Determine the largest size after the transformation measureSize = ComputeLargestTransformedSize(availableSize); } else { // Previous measure/arrange pass determined that // Child.DesiredSize was larger than believed. measureSize = _childActualSize; } // Perform a mesaure on the _layoutRoot (containing Child) _layoutRoot.Measure(measureSize); // WPF equivalent of _childActualSize technique (much simpler, but // doesn't work on Silverlight 2). If the child is going to render // larger than the available size, re-measure according to that size. ////child.Arrange(new Rect()); ////if (child.RenderSize != child.DesiredSize) ////{ //// _layoutRoot.Measure(child.RenderSize); ////} // Transform DesiredSize to find its width/height Rect transformedDesiredRect = RectTransform(new Rect(0, 0, _layoutRoot.DesiredSize.Width, _layoutRoot.DesiredSize.Height), _transformation); Size transformedDesiredSize = new Size(transformedDesiredRect.Width, transformedDesiredRect.Height); // Return result to allocate enough space for the transformation return transformedDesiredSize; } /// /// Provides the behavior for the "Arrange" pass of layout. /// /// The final area within the parent that this /// element should use to arrange itself and its children. /// The actual size used. /// /// Using the WPF paramater name finalSize instead of Silverlight's /// finalSize for clarity. /// protected override Size ArrangeOverride(Size finalSize) { FrameworkElement child = Child; if ((null == _layoutRoot) || (null == child)) { // No child, use whatever was given return finalSize; } // Determine the largest available size after the transformation Size finalSizeTransformed = ComputeLargestTransformedSize(finalSize); if (IsSizeSmaller(finalSizeTransformed, _layoutRoot.DesiredSize)) { // Some elements do not like being given less space than they asked for (ex: TextBlock) // Bump the working size up to do the right thing by them finalSizeTransformed = _layoutRoot.DesiredSize; } // Transform the working size to find its width/height Rect transformedRect = RectTransform(new Rect(0, 0, finalSizeTransformed.Width, finalSizeTransformed.Height), _transformation); // Create the Arrange rect to center the transformed content Rect finalRect = new Rect( -transformedRect.Left + ((finalSize.Width - transformedRect.Width) / 2), -transformedRect.Top + ((finalSize.Height - transformedRect.Height) / 2), finalSizeTransformed.Width, finalSizeTransformed.Height); // Perform an Arrange on _layoutRoot (containing Child) _layoutRoot.Arrange(finalRect); // This is the first opportunity under Silverlight to find out the Child's true DesiredSize if (IsSizeSmaller(finalSizeTransformed, child.RenderSize) && (Size.Empty == _childActualSize)) { // Unfortunately, all the work so far is invalid because the wrong DesiredSize was used // Make a note of the actual DesiredSize _childActualSize = new Size(child.ActualWidth, child.ActualHeight); // Force a new measure/arrange pass InvalidateMeasure(); } else { // Clear the "need to measure/arrange again" flag _childActualSize = Size.Empty; } // Return result to perform the transformation return finalSize; } /// /// Computes the largest usable size after applying the transformation /// to the specified bounds. /// /// The size to arrange within. /// The size required. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Closely corresponds to WPF's FrameworkElement.FindMaximalAreaLocalSpaceRect.")] private Size ComputeLargestTransformedSize(Size arrangeBounds) { // Computed largest transformed size Size computedSize = Size.Empty; // Detect infinite bounds and constrain the scenario bool infiniteWidth = double.IsInfinity(arrangeBounds.Width); if (infiniteWidth) { arrangeBounds.Width = arrangeBounds.Height; } bool infiniteHeight = double.IsInfinity(arrangeBounds.Height); if (infiniteHeight) { arrangeBounds.Height = arrangeBounds.Width; } // Capture the matrix parameters double a = _transformation.M11; double b = _transformation.M12; double c = _transformation.M21; double d = _transformation.M22; // Compute maximum possible transformed width/height based on starting width/height // These constraints define two lines in the positive x/y quadrant double maxWidthFromWidth = Math.Abs(arrangeBounds.Width / a); double maxHeightFromWidth = Math.Abs(arrangeBounds.Width / c); double maxWidthFromHeight = Math.Abs(arrangeBounds.Height / b); double maxHeightFromHeight = Math.Abs(arrangeBounds.Height / d); // The transformed width/height that maximize the area under each segment is its midpoint // At most one of the two midpoints will satisfy both constraints double idealWidthFromWidth = maxWidthFromWidth / 2; double idealHeightFromWidth = maxHeightFromWidth / 2; double idealWidthFromHeight = maxWidthFromHeight / 2; double idealHeightFromHeight = maxHeightFromHeight / 2; // Compute slope of both constraint lines double slopeFromWidth = -(maxHeightFromWidth / maxWidthFromWidth); double slopeFromHeight = -(maxHeightFromHeight / maxWidthFromHeight); if ((0 == arrangeBounds.Width) || (0 == arrangeBounds.Height)) { // Check for empty bounds computedSize = new Size(0, 0); } else if (infiniteWidth && infiniteHeight) { // Check for completely unbound scenario computedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); } else if (!MatrixHasInverse(_transformation)) { // Check for singular matrix computedSize = new Size(0, 0); } else if ((0 == b) || (0 == c)) { // Check for 0/180 degree special cases double maxHeight = (infiniteHeight ? double.PositiveInfinity : maxHeightFromHeight); double maxWidth = (infiniteWidth ? double.PositiveInfinity : maxWidthFromWidth); if ((0 == b) && (0 == c)) { // No constraints computedSize = new Size(maxWidth, maxHeight); } else if (0 == b) { // Constrained by width double computedHeight = Math.Min(idealHeightFromWidth, maxHeight); computedSize = new Size( maxWidth - Math.Abs((c * computedHeight) / a), computedHeight); } else if (0 == c) { // Constrained by height double computedWidth = Math.Min(idealWidthFromHeight, maxWidth); computedSize = new Size( computedWidth, maxHeight - Math.Abs((b * computedWidth) / d)); } } else if ((0 == a) || (0 == d)) { // Check for 90/270 degree special cases double maxWidth = (infiniteHeight ? double.PositiveInfinity : maxWidthFromHeight); double maxHeight = (infiniteWidth ? double.PositiveInfinity : maxHeightFromWidth); if ((0 == a) && (0 == d)) { // No constraints computedSize = new Size(maxWidth, maxHeight); } else if (0 == a) { // Constrained by width double computedHeight = Math.Min(idealHeightFromHeight, maxHeight); computedSize = new Size( maxWidth - Math.Abs((d * computedHeight) / b), computedHeight); } else if (0 == d) { // Constrained by height. double computedWidth = Math.Min(idealWidthFromWidth, maxWidth); computedSize = new Size( computedWidth, maxHeight - Math.Abs((a * computedWidth) / c)); } } else if (idealHeightFromWidth <= ((slopeFromHeight * idealWidthFromWidth) + maxHeightFromHeight)) { // Check the width midpoint for viability (by being below the // height constraint line). computedSize = new Size(idealWidthFromWidth, idealHeightFromWidth); } else if (idealHeightFromHeight <= ((slopeFromWidth * idealWidthFromHeight) + maxHeightFromWidth)) { // Check the height midpoint for viability (by being below the // width constraint line). computedSize = new Size(idealWidthFromHeight, idealHeightFromHeight); } else { // Neither midpoint is viable; use the intersection of the two // constraint lines instead. // Compute width by setting heights equal (m1*x+c1=m2*x+c2). double computedWidth = (maxHeightFromHeight - maxHeightFromWidth) / (slopeFromWidth - slopeFromHeight); // Compute height from width constraint line (y=m*x+c; using // height would give same result). computedSize = new Size( computedWidth, (slopeFromWidth * computedWidth) + maxHeightFromWidth); } return computedSize; } /// /// Return true if Size a is smaller than Size b in either dimension. /// /// The left size. /// The right size. /// A value indicating whether the left size is smaller than /// the right. private static bool IsSizeSmaller(Size a, Size b) { // WPF equivalent of following code: // return ((a.Width < b.Width) || (a.Height < b.Height)); return ((a.Width + AcceptableDelta < b.Width) || (a.Height + AcceptableDelta < b.Height)); } /// /// Rounds the non-offset elements of a matrix to avoid issues due to /// floating point imprecision. /// /// The matrix to round. /// The number of decimals after the /// round. /// The rounded matrix. private static Matrix RoundMatrix(Matrix matrix, int decimalsAfterRound) { return new Matrix( Math.Round(matrix.M11, decimalsAfterRound), Math.Round(matrix.M12, decimalsAfterRound), Math.Round(matrix.M21, decimalsAfterRound), Math.Round(matrix.M22, decimalsAfterRound), matrix.OffsetX, matrix.OffsetY); } /// /// Implement Windows Presentation Foundation's Rect.Transform on /// Silverlight. /// /// The rectangle to transform. /// The matrix to use to transform the rectangle. /// /// The transformed rectangle. private static Rect RectTransform(Rect rectangle, Matrix matrix) { // WPF equivalent of following code: // var rectTransformed = Rect.Transform(rect, matrix); Point leftTop = matrix.Transform(new Point(rectangle.Left, rectangle.Top)); Point rightTop = matrix.Transform(new Point(rectangle.Right, rectangle.Top)); Point leftBottom = matrix.Transform(new Point(rectangle.Left, rectangle.Bottom)); Point rightBottom = matrix.Transform(new Point(rectangle.Right, rectangle.Bottom)); double left = Math.Min(Math.Min(leftTop.X, rightTop.X), Math.Min(leftBottom.X, rightBottom.X)); double top = Math.Min(Math.Min(leftTop.Y, rightTop.Y), Math.Min(leftBottom.Y, rightBottom.Y)); double right = Math.Max(Math.Max(leftTop.X, rightTop.X), Math.Max(leftBottom.X, rightBottom.X)); double bottom = Math.Max(Math.Max(leftTop.Y, rightTop.Y), Math.Max(leftBottom.Y, rightBottom.Y)); Rect rectTransformed = new Rect(left, top, right - left, bottom - top); return rectTransformed; } /// /// Implements Windows Presentation Foundation's Matrix.Multiply on /// Silverlight. /// /// The left matrix. /// The right matrix. /// The product of the two matrices. private static Matrix MatrixMultiply(Matrix matrix1, Matrix matrix2) { // WPF equivalent of following code: // return Matrix.Multiply(matrix1, matrix2); return new Matrix( (matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21), (matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22), (matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21), (matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22), ((matrix1.OffsetX * matrix2.M11) + (matrix1.OffsetY * matrix2.M21)) + matrix2.OffsetX, ((matrix1.OffsetX * matrix2.M12) + (matrix1.OffsetY * matrix2.M22)) + matrix2.OffsetY); } /// /// Implements Windows Presentation Foundation's Matrix.HasInverse on /// Silverlight. /// /// The matrix. /// True if matrix has an inverse. private static bool MatrixHasInverse(Matrix matrix) { // WPF equivalent of following code: // return matrix.HasInverse; return (0 != ((matrix.M11 * matrix.M22) - (matrix.M12 * matrix.M21))); } } } ================================================ FILE: WpfToolkit/DataVisualization/Legend/Legend.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization { /// /// Represents a control that displays a list of items and has a title. /// /// Preview [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(ContentPresenter))] [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] public partial class Legend : HeaderedItemsControl { #if !SILVERLIGHT /// /// Initializes the static members of the Legend class. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static Legend() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Legend), new FrameworkPropertyMetadata(typeof(Legend))); } #endif /// /// Initializes a new instance of the Legend class. /// public Legend() { #if SILVERLIGHT DefaultStyleKey = typeof(Legend); #endif // By default, the Visibility property should follow ContentVisibility - but users can override it SetBinding(VisibilityProperty, new Binding("ContentVisibility") { Source = this }); } /// /// Gets or sets the Style of the ISeriesHost's Title. /// public Style TitleStyle { get { return GetValue(TitleStyleProperty) as Style; } set { SetValue(TitleStyleProperty, value); } } /// /// Identifies the TitleStyle dependency property. /// public static readonly DependencyProperty TitleStyleProperty = DependencyProperty.Register( "TitleStyle", typeof(Style), typeof(Legend), null); /// /// Gets the Visibility of the Legend's content (title and items). /// public Visibility ContentVisibility { get { return (Visibility)GetValue(ContentVisibilityProperty); } protected set { SetValue(ContentVisibilityProperty, value); } } /// /// Identifies the ContentVisibility dependency property. /// public static readonly DependencyProperty ContentVisibilityProperty = DependencyProperty.Register( "ContentVisibility", typeof(Visibility), typeof(Legend), null); /// /// Handles the OnHeaderChanged event for HeaderedItemsControl. /// /// Old header. /// New header. protected override void OnHeaderChanged(object oldHeader, object newHeader) { base.OnHeaderChanged(oldHeader, newHeader); UpdateContentVisibility(); } /// /// Handles the CollectionChanged event for HeaderedItemsControl's ItemsSource. /// /// Event arguments. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { base.OnItemsChanged(e); UpdateContentVisibility(); } /// /// Updates the ContentVisibility property to reflect the presence of content. /// private void UpdateContentVisibility() { ContentVisibility = (Header != null || Items.Count > 0) ? Visibility.Visible : Visibility.Collapsed; } } } ================================================ FILE: WpfToolkit/DataVisualization/NoResetObservableCollection.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Collections; namespace System.Windows.Controls.DataVisualization { /// /// An observable collection that cannot be reset. When clear is called /// items are removed individually, giving listeners the chance to detect /// each remove event and perform operations such as unhooking event /// handlers. /// /// The type of item in the collection. internal class NoResetObservableCollection : ObservableCollection { /// /// Instantiates a new instance of the NoResetObservableCollection /// class. /// public NoResetObservableCollection() { } /// /// Clears all items in the collection by removing them individually. /// protected override void ClearItems() { IList items = new List(this); foreach (T item in items) { Remove(item); } } } } ================================================ FILE: WpfToolkit/DataVisualization/ObjectPool.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; namespace System.Windows.Controls.DataVisualization { /// /// A pool of objects that can be reused. /// /// The type of object in the pool. internal class ObjectPool { /// /// The default minimum number objects to keep in the pool. /// private const int DefaultMinimumObjectsInThePool = 10; #if DEBUG /// /// A value indicating whether the pool is being traversed. /// private bool _traversing = false; #endif /// /// A function which creates objects. /// private Func _createObject; /// /// The list of objects. /// private List _objects; /// /// The index of the current item in the list. /// private int currentIndex = 0; /// /// The minimum number of objects to keep in the pool. /// private int minimumObjectsInThePool; /// /// Initializes a new instance of the ObjectPool class. /// /// The minimum number of objects /// to keep in the pool. /// The function that creates the objects. /// public ObjectPool( int minimumObjectsInThePool, Func createObject) { this._objects = new List(minimumObjectsInThePool); this.minimumObjectsInThePool = minimumObjectsInThePool; this._createObject = createObject; Reset(); } /// /// Initializes a new instance of the ObjectPool class. /// /// The function that creates the objects. /// public ObjectPool(Func createObject) : this(DefaultMinimumObjectsInThePool, createObject) { } /// /// Performs an operation on the subsequent, already-created objects /// in the pool. /// /// The action to perform on the remaining objects. /// public void ForEachRemaining(Action action) { for (var cnt = currentIndex; cnt < _objects.Count; cnt++) { action(_objects[cnt]); } } /// /// Creates a new object or reuses an existing object in the pool. /// /// A new or existing object in the pool. public T Next() { if (currentIndex == _objects.Count) { _objects.Add(_createObject()); } T item = _objects[currentIndex]; currentIndex++; return item; } /// /// Resets the pool of objects. /// public void Reset() { #if DEBUG _traversing = true; #endif currentIndex = 0; } /// /// Finishes the object creation process. /// /// /// If there are substantially more remaining objects in the pool those /// objects may be removed. /// public void Done() { #if DEBUG _traversing = false; #endif // Remove the rest of the objects if we are using less than // half of the pool. if (currentIndex != 0 && _objects.Count > 0 && currentIndex >= minimumObjectsInThePool && currentIndex < _objects.Count / 2) { _objects.RemoveRange(currentIndex, _objects.Count - currentIndex); } } /// /// Removes the objects from the pool. /// public void Clear() { #if DEBUG System.Diagnostics.Debug.Assert(!_traversing, "Cannot clear an object pool while it is being traversed."); #endif _objects.Clear(); } } } ================================================ FILE: WpfToolkit/DataVisualization/ObservableCollectionListAdapter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Windows; using System.Windows.Controls; namespace System.Windows.Controls.DataVisualization { /// /// An object that synchronizes changes in an observable collection to /// a list. /// /// The type of the objects in the collection. /// internal class ObservableCollectionListAdapter where T : class { /// /// The collection to synchronize with a list. /// private IEnumerable _collection; /// /// Gets or sets the collection to synchronize with a list. /// public IEnumerable Collection { get { return _collection; } set { IEnumerable oldValue = _collection; INotifyCollectionChanged oldObservableCollection = oldValue as INotifyCollectionChanged; INotifyCollectionChanged newObservableCollection = value as INotifyCollectionChanged; _collection = value; if (oldObservableCollection != null) { oldObservableCollection.CollectionChanged -= OnCollectionChanged; } if (value == null && TargetList != null) { TargetList.Clear(); } if (newObservableCollection != null) { newObservableCollection.CollectionChanged += OnCollectionChanged; } } } /// /// Gets or sets the panel to synchronize with the collection. /// public IList TargetList { get; set; } /// /// Method that synchronizes the panel's child collection with the /// contents of the observable collection when it changes. /// /// The source of the event. /// Information about the event. public void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (TargetList != null) { if (e.Action == NotifyCollectionChangedAction.Reset) { TargetList.Clear(); } else if (e.Action == NotifyCollectionChangedAction.Replace) { for (int cnt = 0; cnt < e.OldItems.Count; cnt++) { T oldItem = e.OldItems[cnt] as T; T newItem = e.NewItems[cnt] as T; int index = TargetList.IndexOf(oldItem); if (index != -1) { TargetList[index] = newItem; } else { TargetList.Remove(oldItem); TargetList.Add(newItem); } } } else { if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null) { foreach (T element in e.OldItems) { TargetList.Remove(element); } } else if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null) { int startingIndex = e.NewStartingIndex; if (startingIndex != 0) { T previousElement = Collection.FastElementAt(startingIndex - 1); int targetListIndex = TargetList.IndexOf(previousElement); if (targetListIndex != -1) { startingIndex = targetListIndex + 1; } } else if (Collection.FastCount() > 1) { T nextElement = Collection.FastElementAt(startingIndex + 1); int targetListIndex = TargetList.IndexOf(nextElement); if (targetListIndex == -1) { startingIndex = 0; } else { startingIndex = targetListIndex; } } e.NewItems .OfType() .ForEachWithIndex((item, index) => { TargetList.Insert(startingIndex + index, item); }); } } } } /// /// A method that populates a panel with the items in the collection. /// public void Populate() { if (TargetList != null) { if (Collection != null) { foreach (T item in Collection) { TargetList.Add(item); } } else { TargetList.Clear(); } } } /// /// Removes the items in the adapted list from the target list. /// public void ClearItems() { foreach (T item in this.Collection) { this.TargetList.Remove(item); } } } } ================================================ FILE: WpfToolkit/DataVisualization/OrientedPanel.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization { /// /// A panel that plots elements on a one dimensional plane. In order to /// minimize collisions it moves elements further and further from the edge /// of the plane based on their priority. Elements that have the same /// priority level are always the same distance from the edge. /// internal class OrientedPanel : Panel { #region public double ActualMinimumDistanceBetweenChildren /// /// Gets the actual minimum distance between children. /// public double ActualMinimumDistanceBetweenChildren { get { return (double)GetValue(ActualMinimumDistanceBetweenChildrenProperty); } private set { SetValue(ActualMinimumDistanceBetweenChildrenProperty, value); } } /// /// Identifies the ActualMinimumDistanceBetweenChildren dependency property. /// public static readonly DependencyProperty ActualMinimumDistanceBetweenChildrenProperty = DependencyProperty.Register( "ActualMinimumDistanceBetweenChildren", typeof(double), typeof(OrientedPanel), new PropertyMetadata(0.0)); #endregion public double ActualMinimumDistanceBetweenChildren #region public double MinimumDistanceBetweenChildren /// /// Gets or sets the minimum distance between children. /// public double MinimumDistanceBetweenChildren { get { return (double)GetValue(MinimumDistanceBetweenChildrenProperty); } set { SetValue(MinimumDistanceBetweenChildrenProperty, value); } } /// /// Identifies the MinimumDistanceBetweenChildren dependency property. /// public static readonly DependencyProperty MinimumDistanceBetweenChildrenProperty = DependencyProperty.Register( "MinimumDistanceBetweenChildren", typeof(double), typeof(OrientedPanel), new PropertyMetadata(0.0, OnMinimumDistanceBetweenChildrenPropertyChanged)); /// /// MinimumDistanceBetweenChildrenProperty property changed handler. /// /// OrientedPanel that changed its MinimumDistanceBetweenChildren. /// Event arguments. private static void OnMinimumDistanceBetweenChildrenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { OrientedPanel source = (OrientedPanel)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnMinimumDistanceBetweenChildrenPropertyChanged(oldValue, newValue); } /// /// MinimumDistanceBetweenChildrenProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnMinimumDistanceBetweenChildrenPropertyChanged(double oldValue, double newValue) { InvalidateMeasure(); } #endregion public double MinimumDistanceBetweenChildren #region public double ActualLength /// /// Gets the actual length of the panel. /// public double ActualLength { get { return (double)GetValue(ActualLengthProperty); } } /// /// Identifies the ActualLength dependency property. /// public static readonly DependencyProperty ActualLengthProperty = DependencyProperty.Register( "ActualLength", typeof(double), typeof(OrientedPanel), new PropertyMetadata(0.0)); #endregion public double ActualLength #region public attached double CenterCoordinate /// /// Gets the value of the CenterCoordinate attached property for a specified UIElement. /// /// The UIElement from which the property value is read. /// The CenterCoordinate property value for the UIElement. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on UIElement's")] public static double GetCenterCoordinate(UIElement element) { if (element == null) { throw new ArgumentNullException("element"); } return (double) element.GetValue(CenterCoordinateProperty); } /// /// Sets the value of the CenterCoordinate attached property to a specified UIElement. /// /// The UIElement to which the attached property is written. /// The needed CenterCoordinate value. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on UIElement's")] public static void SetCenterCoordinate(UIElement element, double value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(CenterCoordinateProperty, value); } /// /// Identifies the CenterCoordinate dependency property. /// public static readonly DependencyProperty CenterCoordinateProperty = DependencyProperty.RegisterAttached( "CenterCoordinate", typeof(double), typeof(OrientedPanel), new PropertyMetadata(OnCenterCoordinatePropertyChanged)); /// /// CenterCoordinateProperty property changed handler. /// /// UIElement that changed its CenterCoordinate. /// Event arguments. public static void OnCenterCoordinatePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) { UIElement source = dependencyObject as UIElement; if (source == null) { throw new ArgumentNullException("dependencyObject"); } OrientedPanel parent = VisualTreeHelper.GetParent(source) as OrientedPanel; if (parent != null) { parent.InvalidateMeasure(); } } #endregion public attached double CenterCoordinate #region public double OffsetPadding /// /// Gets or sets the amount of offset padding to add between items. /// public double OffsetPadding { get { return (double)GetValue(OffsetPaddingProperty); } set { SetValue(OffsetPaddingProperty, value); } } /// /// Identifies the OffsetPadding dependency property. /// public static readonly DependencyProperty OffsetPaddingProperty = DependencyProperty.Register( "OffsetPadding", typeof(double), typeof(OrientedPanel), new PropertyMetadata(0.0, OnOffsetPaddingPropertyChanged)); /// /// OffsetPaddingProperty property changed handler. /// /// OrientedPanel that changed its OffsetPadding. /// Event arguments. private static void OnOffsetPaddingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { OrientedPanel source = (OrientedPanel)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnOffsetPaddingPropertyChanged(oldValue, newValue); } /// /// OffsetPaddingProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnOffsetPaddingPropertyChanged(double oldValue, double newValue) { this.InvalidateMeasure(); } #endregion public double OffsetPadding #region public attached int Priority /// /// Gets the value of the Priority attached property for a specified UIElement. /// /// The UIElement from which the property value is read. /// The Priority property value for the UIElement. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on UIElement's")] public static int GetPriority(UIElement element) { if (element == null) { throw new ArgumentNullException("element"); } return (int) element.GetValue(PriorityProperty); } /// /// Sets the value of the Priority attached property to a specified UIElement. /// /// The UIElement to which the attached property is written. /// The needed Priority value. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on UIElement's")] public static void SetPriority(UIElement element, int value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(PriorityProperty, value); } /// /// Identifies the Priority dependency property. /// public static readonly DependencyProperty PriorityProperty = DependencyProperty.RegisterAttached( "Priority", typeof(int), typeof(OrientedPanel), new PropertyMetadata(OnPriorityPropertyChanged)); /// /// PriorityProperty property changed handler. /// /// UIElement that changed its Priority. /// Event arguments. public static void OnPriorityPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) { UIElement source = dependencyObject as UIElement; if (source == null) { throw new ArgumentNullException("dependencyObject"); } OrientedPanel parent = VisualTreeHelper.GetParent(source) as OrientedPanel; if (parent != null) { parent.InvalidateMeasure(); } } #endregion public attached int Priority #region public bool IsInverted /// /// Gets or sets a value indicating whether the panel is inverted. /// public bool IsInverted { get { return (bool)GetValue(IsInvertedProperty); } set { SetValue(IsInvertedProperty, value); } } /// /// Identifies the IsInverted dependency property. /// public static readonly DependencyProperty IsInvertedProperty = DependencyProperty.Register( "IsInverted", typeof(bool), typeof(OrientedPanel), new PropertyMetadata(false, OnIsInvertedPropertyChanged)); /// /// IsInvertedProperty property changed handler. /// /// OrientedPanel that changed its IsInverted. /// Event arguments. private static void OnIsInvertedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { OrientedPanel source = (OrientedPanel)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsInvertedPropertyChanged(oldValue, newValue); } /// /// IsInvertedProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsInvertedPropertyChanged(bool oldValue, bool newValue) { InvalidateMeasure(); } #endregion public bool IsInverted #region public bool IsReversed /// /// Gets or sets a value indicating whether the direction is reversed. /// public bool IsReversed { get { return (bool)GetValue(IsReversedProperty); } set { SetValue(IsReversedProperty, value); } } /// /// Identifies the IsReversed dependency property. /// public static readonly DependencyProperty IsReversedProperty = DependencyProperty.Register( "IsReversed", typeof(bool), typeof(OrientedPanel), new PropertyMetadata(false, OnIsReversedPropertyChanged)); /// /// IsReversedProperty property changed handler. /// /// OrientedPanel that changed its IsReversed. /// Event arguments. private static void OnIsReversedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { OrientedPanel source = (OrientedPanel)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsReversedPropertyChanged(oldValue, newValue); } /// /// IsReversedProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsReversedPropertyChanged(bool oldValue, bool newValue) { InvalidateMeasure(); } #endregion public bool IsReversed #region public Orientation Orientation /// /// Gets or sets the orientation of the panel. /// public Orientation Orientation { get { return (Orientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } /// /// Identifies the Orientation dependency property. /// public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( "Orientation", typeof(Orientation), typeof(OrientedPanel), new PropertyMetadata(Orientation.Horizontal, OnOrientationPropertyChanged)); /// /// OrientationProperty property changed handler. /// /// OrientedPanel that changed its Orientation. /// Event arguments. private static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { OrientedPanel source = (OrientedPanel)d; Orientation newValue = (Orientation)e.NewValue; source.OnOrientationPropertyChanged(newValue); } /// /// OrientationProperty property changed handler. /// /// New value. protected virtual void OnOrientationPropertyChanged(Orientation newValue) { UpdateActualLength(); InvalidateMeasure(); } #endregion public Orientation Orientation /// /// Gets or sets the offset of the edge to use for each priority group. /// private IDictionary PriorityOffsets { get; set; } /// /// Instantiates a new instance of the OrientedPanel class. /// public OrientedPanel() { UpdateActualLength(); } /// /// Updates the actual length property. /// private void UpdateActualLength() { this.SetBinding(ActualLengthProperty, new Binding((Orientation == Orientation.Horizontal) ? "ActualWidth" : "ActualHeight") { Source = this }); } /// /// Returns a sequence of ranges for a given sequence of children and a /// length selector. /// /// A sequence of children. /// A function that returns a length given /// a UIElement. /// A sequence of ranges. private static IEnumerable> GetRanges(IEnumerable children, Func lengthSelector) { return children .Select(child => { double centerCoordinate = GetCenterCoordinate(child); double halfLength = lengthSelector(child) / 2; return new Range(centerCoordinate - halfLength, centerCoordinate + halfLength); }); } /// /// Measures children and determines necessary size. /// /// The available size. /// The necessary size. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq use artificially increases cyclomatic complexity. Linq functions are well-understood.")] protected override Size MeasureOverride(Size availableSize) { double offset = 0.0; if (Children.Count > 0) { Size totalSize = new Size(double.PositiveInfinity, double.PositiveInfinity); foreach (UIElement child in this.Children) { child.Measure(totalSize); } Func lengthSelector = null; Func offsetSelector = null; if (Orientation == Orientation.Horizontal) { lengthSelector = child => GetCorrectedDesiredSize(child).Width; offsetSelector = child => GetCorrectedDesiredSize(child).Height; } else { lengthSelector = child => GetCorrectedDesiredSize(child).Height; offsetSelector = child => GetCorrectedDesiredSize(child).Width; } IEnumerable> priorityGroups = from child in Children.CastWrapper() group child by GetPriority(child) into priorityGroup select priorityGroup; ActualMinimumDistanceBetweenChildren = (from priorityGroup in priorityGroups let orderedElements = (from element in priorityGroup orderby GetCenterCoordinate(element) ascending select element).ToList() where orderedElements.Count >= 2 select (EnumerableFunctions.Zip( orderedElements, orderedElements.Skip(1), (leftElement, rightElement) => { double halfLeftLength = lengthSelector(leftElement) / 2; double leftCenterCoordinate = GetCenterCoordinate(leftElement); double halfRightLength = lengthSelector(rightElement) / 2; double rightCenterCoordinate = GetCenterCoordinate(rightElement); return (rightCenterCoordinate - halfRightLength) - (leftCenterCoordinate + halfLeftLength); })) .Min()) .MinOrNullable() ?? MinimumDistanceBetweenChildren; IEnumerable priorities = Children .CastWrapper() .Select(child => GetPriority(child)).Distinct().OrderBy(priority => priority).ToList(); PriorityOffsets = new Dictionary(); foreach (int priority in priorities) { PriorityOffsets[priority] = 0.0; } IEnumerable> priorityPairs = EnumerableFunctions.Zip(priorities, priorities.Skip(1), (previous, next) => new Tuple(previous, next)); foreach (Tuple priorityPair in priorityPairs) { IEnumerable currentPriorityChildren = Children.CastWrapper().Where(child => GetPriority(child) == priorityPair.Item1).ToList(); IEnumerable> currentPriorityRanges = GetRanges(currentPriorityChildren, lengthSelector); IEnumerable nextPriorityChildren = Children.CastWrapper().Where(child => GetPriority(child) == priorityPair.Item2).ToList(); IEnumerable> nextPriorityRanges = GetRanges(nextPriorityChildren, lengthSelector); bool intersects = (from currentPriorityRange in currentPriorityRanges from nextPriorityRange in nextPriorityRanges select currentPriorityRange.IntersectsWith(nextPriorityRange)) .Any(value => value); if (intersects) { double maxCurrentPriorityChildOffset = currentPriorityChildren .Select(child => offsetSelector(child)) .MaxOrNullable() ?? 0.0; offset += maxCurrentPriorityChildOffset + OffsetPadding; } PriorityOffsets[priorityPair.Item2] = offset; } offset = (Children .CastWrapper() .GroupBy(child => GetPriority(child)) .Select( group => group .Select(child => PriorityOffsets[group.Key] + offsetSelector(child)) .MaxOrNullable())) .Where(num => num.HasValue) .Select(num => num.Value) .MaxOrNullable() ?? 0.0; } if (Orientation == Orientation.Horizontal) { return new Size(0, offset); } else { return new Size(offset, 0); } } /// /// Arranges items according to position and priority. /// /// The final size of the panel. /// The final size of the control. protected override Size ArrangeOverride(Size finalSize) { foreach (UIElement child in Children) { double x = 0.0; double y = 0.0; x = GetCenterCoordinate(child); y = PriorityOffsets[GetPriority(child)]; double totalLength = 0.0; double totalOffsetLength = 0.0; double length = 0.0; double offsetLength = 0.0; Size childCorrectedDesiredSize = GetCorrectedDesiredSize(child); if (Orientation == Orientation.Horizontal) { totalLength = finalSize.Width; length = childCorrectedDesiredSize.Width; offsetLength = childCorrectedDesiredSize.Height; totalOffsetLength = finalSize.Height; } else if (Orientation == Orientation.Vertical) { totalLength = finalSize.Height; length = childCorrectedDesiredSize.Height; offsetLength = childCorrectedDesiredSize.Width; totalOffsetLength = finalSize.Width; } double halfLength = length / 2; double left = 0.0; double top = 0.0; if (!IsReversed) { left = x - halfLength; } else { left = totalLength - Math.Round(x + halfLength); } if (!IsInverted) { top = y; } else { top = totalOffsetLength - Math.Round(y + offsetLength); } left = Math.Min(Math.Round(left), totalLength - 1); top = Math.Round(top); if (Orientation == Orientation.Horizontal) { child.Arrange(new Rect(left, top, length, offsetLength)); } else if (Orientation == Orientation.Vertical) { child.Arrange(new Rect(top, left, offsetLength, length)); } } return finalSize; } /// /// Gets the "corrected" DesiredSize (for Line instances); one that is /// more consistent with how the elements actually render. /// /// UIElement to get the size for. /// Corrected size. private static Size GetCorrectedDesiredSize(UIElement element) { Line elementAsLine = element as Line; if (null != elementAsLine) { return new Size( Math.Max(elementAsLine.StrokeThickness, elementAsLine.X2 - elementAsLine.X1), Math.Max(elementAsLine.StrokeThickness, elementAsLine.Y2 - elementAsLine.Y1)); } else { return element.DesiredSize; } } } } ================================================ FILE: WpfToolkit/DataVisualization/Properties/AssemblyInfo.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Security; using System.Windows; // WPF-only settings [assembly: ThemeInfo( ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] // WPF Toolkit settings [assembly: AllowPartiallyTrustedCallers] //[assembly: SecurityCritical] ================================================ FILE: WpfToolkit/DataVisualization/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.21006.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Windows.Controls.DataVisualization.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Windows.Controls.DataVisualization.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Changing the Chart's Axes property is not supported; changes to the collection should be made with its .Add/.Remove methods instead.. /// internal static string Chart_Axes_SetterNotSupported { get { return ResourceManager.GetString("Chart_Axes_SetterNotSupported", resourceCulture); } } /// /// Looks up a localized string similar to Axis location cannot be changed to auto when hosted inside of a series host.. /// internal static string Chart_AxisLocationChanged_CantBeChangedToAutoWhenHostedInsideOfASeriesHost { get { return ResourceManager.GetString("Chart_AxisLocationChanged_CantBeChangedToAutoWhenHostedInsideOfASeriesHost", resourceCulture); } } /// /// Looks up a localized string similar to Changing the Chart's Series property is not supported; changes to the collection should be made with its .Add/.Remove methods instead.. /// internal static string Chart_Series_SetterNotSupported { get { return ResourceManager.GetString("Chart_Series_SetterNotSupported", resourceCulture); } } /// /// Looks up a localized string similar to The content property must be set to a Chart object.. /// internal static string DataPointSeriesDropTarget_set_Content_ContentMustBeAChart { get { return ResourceManager.GetString("DataPointSeriesDropTarget_set_Content_ContentMustBeAChart", resourceCulture); } } /// /// Looks up a localized string similar to Assigned dependent axis cannot be used. This may be due to an unset Orientation property for the axis or a type mismatch between the values being plotted and those supported by the axis.. /// internal static string DataPointSeriesWithAxes_GetAxes_AssignedDependentAxisCannotBeUsed { get { return ResourceManager.GetString("DataPointSeriesWithAxes_GetAxes_AssignedDependentAxisCannotBeUsed", resourceCulture); } } /// /// Looks up a localized string similar to Assigned independent axis cannot be used. This may be due to an unset Orientation property for the axis.. /// internal static string DataPointSeriesWithAxes_GetAxes_AssignedIndependentAxisCannotBeUsed { get { return ResourceManager.GetString("DataPointSeriesWithAxes_GetAxes_AssignedIndependentAxisCannotBeUsed", resourceCulture); } } /// /// Looks up a localized string similar to No suitable axis is available for plotting the dependent value.. /// internal static string DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue { get { return ResourceManager.GetString("DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue", resourceCulture); } } /// /// Looks up a localized string similar to This series does not support radial axes.. /// internal static string DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes { get { return ResourceManager.GetString("DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes", resourceCulture); } } /// /// Looks up a localized string similar to A SeriesDefinition does not have its DependentValueBinding or DependentValuePath properties set; unable to determine the source for dependent values. Please use either of these properties to identify the data model property corresponding to the dependent values.. /// internal static string DefinitionSeries_EnsureAxes_MissingDependentValueBinding { get { return ResourceManager.GetString("DefinitionSeries_EnsureAxes_MissingDependentValueBinding", resourceCulture); } } /// /// Looks up a localized string similar to A SeriesDefinition does not have its IndependentValueBinding or IndependentValuePath properties set; unable to determine the source for independent values. Please use either of these properties to identify the data model property corresponding to the independent values.. /// internal static string DefinitionSeries_EnsureAxes_MissingIndependentValueBinding { get { return ResourceManager.GetString("DefinitionSeries_EnsureAxes_MissingIndependentValueBinding", resourceCulture); } } /// /// Looks up a localized string similar to Changing the SeriesDefinitions property of a DefinitionSeries is not supported; changes to the collection should be made with its .Add/.Remove methods instead.. /// internal static string DefinitionSeries_SeriesDefinitions_SetterNotSupported { get { return ResourceManager.GetString("DefinitionSeries_SeriesDefinitions_SetterNotSupported", resourceCulture); } } /// /// Looks up a localized string similar to Cannot determine the size of an axis without an orientation.. /// internal static string DisplayAxis_GetLength_CannotDetermineTheLengthOfAnAxisWithAnOrientationOfNone { get { return ResourceManager.GetString("DisplayAxis_GetLength_CannotDetermineTheLengthOfAnAxisWithAnOrientationOfNone", resourceCulture); } } /// /// Looks up a localized string similar to Attempt to set Edge property to an invalid value.. /// internal static string EdgePanel_OnEdgePropertyChanged { get { return ResourceManager.GetString("EdgePanel_OnEdgePropertyChanged", resourceCulture); } } /// /// Looks up a localized string similar to The DataRangeBinding property of the Interpolator must be bound to an IConvertible object.. /// internal static string Interpolator_IncludeInRange_DataRangeBindingNotIConvertible { get { return ResourceManager.GetString("Interpolator_IncludeInRange_DataRangeBindingNotIConvertible", resourceCulture); } } /// /// Looks up a localized string similar to The maximum value must be larger than or equal to the minimum value.. /// internal static string Range_ctor_MaximumValueMustBeLargerThanOrEqualToMinimumValue { get { return ResourceManager.GetString("Range_ctor_MaximumValueMustBeLargerThanOrEqualToMinimumValue", resourceCulture); } } /// /// Looks up a localized string similar to "Cannot read the Maximum of an empty range.". /// internal static string Range_get_Maximum_CannotReadTheMaximumOfAnEmptyRange { get { return ResourceManager.GetString("Range_get_Maximum_CannotReadTheMaximumOfAnEmptyRange", resourceCulture); } } /// /// Looks up a localized string similar to "Cannot read the Minimum of an empty range.". /// internal static string Range_get_Minimum_CannotReadTheMinimumOfAnEmptyRange { get { return ResourceManager.GetString("Range_get_Minimum_CannotReadTheMinimumOfAnEmptyRange", resourceCulture); } } /// /// Looks up a localized string similar to [{0},{1}]. /// internal static string Range_ToString_Data { get { return ResourceManager.GetString("Range_ToString_Data", resourceCulture); } } /// /// Looks up a localized string similar to No Data.. /// internal static string Range_ToString_NoData { get { return ResourceManager.GetString("Range_ToString_NoData", resourceCulture); } } /// /// Looks up a localized string similar to The maximum value must be larger than or equal to the minimum value.. /// internal static string RangeAxis_MaximumValueMustBeLargerThanOrEqualToMinimumValue { get { return ResourceManager.GetString("RangeAxis_MaximumValueMustBeLargerThanOrEqualToMinimumValue", resourceCulture); } } /// /// Looks up a localized string similar to The minimum value must be smaller than or equal to the maximum value.. /// internal static string RangeAxis_MinimumValueMustBeLargerThanOrEqualToMaximumValue { get { return ResourceManager.GetString("RangeAxis_MinimumValueMustBeLargerThanOrEqualToMaximumValue", resourceCulture); } } /// /// Looks up a localized string similar to Collection is read-only.. /// internal static string ReadOnlyObservableCollection_CollectionIsReadOnly { get { return ResourceManager.GetString("ReadOnlyObservableCollection_CollectionIsReadOnly", resourceCulture); } } /// /// Looks up a localized string similar to Cannot reset a ResourceDictionaryEnumerator; reset the ResourceDictionaryDispenser instead.. /// internal static string ResourceDictionaryEnumerator_CantResetEnumeratorResetDispenserInstead { get { return ResourceManager.GetString("ResourceDictionaryEnumerator_CantResetEnumeratorResetDispenserInstead", resourceCulture); } } /// /// Looks up a localized string similar to Series {0}. /// internal static string Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString { get { return ResourceManager.GetString("Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString", resourceCulture); } } /// /// Looks up a localized string similar to A series cannot be added to more than one series host.. /// internal static string Series_SeriesHost_SeriesHostPropertyNotNull { get { return ResourceManager.GetString("Series_SeriesHost_SeriesHostPropertyNotNull", resourceCulture); } } /// /// Looks up a localized string similar to A SeriesDefinition instance's SeriesHost property may only be set to an instance of the DefinitionSeries class; other SeriesHost types are not supported.. /// internal static string SeriesDefinition_SeriesHost_InvalidParent { get { return ResourceManager.GetString("SeriesDefinition_SeriesHost_InvalidParent", resourceCulture); } } /// /// Looks up a localized string similar to Invalid attempt to remove permanent axis from axis collection.. /// internal static string SeriesHostAxesCollection_InvalidAttemptToRemovePermanentAxisFromSeriesHost { get { return ResourceManager.GetString("SeriesHostAxesCollection_InvalidAttemptToRemovePermanentAxisFromSeriesHost", resourceCulture); } } /// /// Looks up a localized string similar to An axis cannot be removed from a series host when one or more hosted series is listening to it.. /// internal static string SeriesHostAxesCollection_RemoveItem_AxisCannotBeRemovedFromASeriesHostWhenOneOrMoreSeriesAreListeningToIt { get { return ResourceManager.GetString("SeriesHostAxesCollection_RemoveItem_AxisCannotBeRemovedFromASeriesHostWhenOneOrMo" + "reSeriesAreListeningToIt", resourceCulture); } } /// /// Looks up a localized string similar to Either ItemTemplate must be set or TreeMapItemDefinitionSelector must return a non-null template.. /// internal static string TreeMap_BuildTreeMapTree_TemplateNotSet { get { return ResourceManager.GetString("TreeMap_BuildTreeMapTree_TemplateNotSet", resourceCulture); } } /// /// Looks up a localized string similar to The value of PropertyBinding property of an interpolator cannot be null.. /// internal static string TreeMap_CreateChildren_InterpolatorBindingNotSet { get { return ResourceManager.GetString("TreeMap_CreateChildren_InterpolatorBindingNotSet", resourceCulture); } } /// /// Looks up a localized string similar to Changing the TreeMap's Interpolators Property is not supported; changes to the collection should be made with its .Add/.Remove methods instead.. /// internal static string TreeMap_Interpolators_SetterNotSupported { get { return ResourceManager.GetString("TreeMap_Interpolators_SetterNotSupported", resourceCulture); } } /// /// Looks up a localized string similar to Invalid attempt to insert a duplicate item.. /// internal static string UniqueObservableCollection_InvalidAttemptToInsertADuplicateItem { get { return ResourceManager.GetString("UniqueObservableCollection_InvalidAttemptToInsertADuplicateItem", resourceCulture); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Changing the Chart's Axes property is not supported; changes to the collection should be made with its .Add/.Remove methods instead. Setter is public to work around a limitation with the XAML editing tools. Changing the Chart's Series property is not supported; changes to the collection should be made with its .Add/.Remove methods instead. Setter is public to work around a limitation with the XAML editing tools. Assigned dependent axis cannot be used. This may be due to an unset Orientation property for the axis or a type mismatch between the values being plotted and those supported by the axis. Exception message. Assigned independent axis cannot be used. This may be due to an unset Orientation property for the axis. Exception message. This series does not support radial axes. Exception message. No suitable axis is available for plotting the dependent value. Exception message. The maximum value must be larger than or equal to the minimum value. Exception message. The minimum value must be smaller than or equal to the maximum value. Exception message. The maximum value must be larger than or equal to the minimum value. Exception message. "Cannot read the Maximum of an empty range." Exception message. "Cannot read the Minimum of an empty range." Exception message. [{0},{1}] Range text for ToString method when data is present. No Data. Range text for ToString method when no data is present. Collection is read-only. Exception message. Invalid attempt to remove permanent axis from axis collection. Exception message. An axis cannot be removed from a series host when one or more hosted series is listening to it. Exception message. Series {0} Used to format the string returned by the Title property if a Title is not specified. Invalid attempt to insert a duplicate item. Exception message. A series cannot be added to more than one series host. Exception message. Attempt to set Edge property to an invalid value. Exception message. Axis location cannot be changed to auto when hosted inside of a series host. Exception message. Cannot determine the size of an axis without an orientation. Exception message. Cannot reset a ResourceDictionaryEnumerator; reset the ResourceDictionaryDispenser instead. Exception message. Either ItemTemplate must be set or TreeMapItemDefinitionSelector must return a non-null template. Exception thrown when the template for the current item is null. The value of PropertyBinding property of an interpolator cannot be null. Exception thrown when the PropertyBinding property of one of the interpolators is null. The DataRangeBinding property of the Interpolator must be bound to an IConvertible object. Exception thrown when an interpolator is bound to a value which is not IConvertible. Changing the TreeMap's Interpolators Property is not supported; changes to the collection should be made with its .Add/.Remove methods instead. Setter is public to work around a limitation with the XAML editing tools. The content property must be set to a Chart object. Exception message. A SeriesDefinition does not have its DependentValueBinding or DependentValuePath properties set; unable to determine the source for dependent values. Please use either of these properties to identify the data model property corresponding to the dependent values. Exception message. A SeriesDefinition does not have its IndependentValueBinding or IndependentValuePath properties set; unable to determine the source for independent values. Please use either of these properties to identify the data model property corresponding to the independent values. Exception message. Changing the SeriesDefinitions property of a DefinitionSeries is not supported; changes to the collection should be made with its .Add/.Remove methods instead. Setter is public to work around a limitation with the XAML editing tools. A SeriesDefinition instance's SeriesHost property may only be set to an instance of the DefinitionSeries class; other SeriesHost types are not supported. Exception message. ================================================ FILE: WpfToolkit/DataVisualization/Range.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Globalization; namespace System.Windows.Controls.DataVisualization { /// /// A range of values. /// /// The type of the values. /// Preview public struct Range where T : IComparable { /// /// A flag that determines whether the range is empty or not. /// private bool _hasData; /// /// Gets a value indicating whether the range is empty or not. /// public bool HasData { get { return _hasData; } } /// /// The maximum value in the range. /// private T _maximum; /// /// Gets the maximum value in the range. /// public T Maximum { get { if (!HasData) { throw new InvalidOperationException(Properties.Resources.Range_get_Maximum_CannotReadTheMaximumOfAnEmptyRange); } return _maximum; } } /// /// The minimum value in the range. /// private T _minimum; /// /// Gets the minimum value in the range. /// public T Minimum { get { if (!HasData) { throw new InvalidOperationException(Properties.Resources.Range_get_Minimum_CannotReadTheMinimumOfAnEmptyRange); } return _minimum; } } /// /// Initializes a new instance of the Range class. /// /// The minimum value. /// The maximum value. public Range(T minimum, T maximum) { if (minimum == null) { throw new ArgumentNullException("minimum"); } if (maximum == null) { throw new ArgumentNullException("maximum"); } _hasData = true; _minimum = minimum; _maximum = maximum; int compareValue = ValueHelper.Compare(minimum, maximum); if (compareValue == 1) { throw new InvalidOperationException(Properties.Resources.Range_ctor_MaximumValueMustBeLargerThanOrEqualToMinimumValue); } } /// /// Compare two ranges and return a value indicating whether they are /// equal. /// /// Left-hand side range. /// Right-hand side range. /// A value indicating whether the ranges are equal. public static bool operator ==(Range leftRange, Range rightRange) { if (!leftRange.HasData) { return !rightRange.HasData; } if (!rightRange.HasData) { return !leftRange.HasData; } return leftRange.Minimum.Equals(rightRange.Minimum) && leftRange.Maximum.Equals(rightRange.Maximum); } /// /// Compare two ranges and return a value indicating whether they are /// not equal. /// /// Left-hand side range. /// Right-hand side range. /// A value indicating whether the ranges are not equal. /// public static bool operator !=(Range leftRange, Range rightRange) { return !(leftRange == rightRange); } /// /// Adds a range to the current range. /// /// A range to add to the current range. /// A new range that encompasses the instance range and the /// range parameter. public Range Add(Range range) { if (!this.HasData) { return range; } else if (!range.HasData) { return this; } T minimum = ValueHelper.Compare(this.Minimum, range.Minimum) == -1 ? this.Minimum : range.Minimum; T maximum = ValueHelper.Compare(this.Maximum, range.Maximum) == 1 ? this.Maximum : range.Maximum; return new Range(minimum, maximum); } /// /// Compares the range to another range. /// /// A different range. /// A value indicating whether the ranges are equal. public bool Equals(Range range) { return this == range; } /// /// Compares the range to an object. /// /// Another object. /// A value indicating whether the other object is a range, /// and if so, whether that range is equal to the instance range. /// public override bool Equals(object obj) { Range range = (Range)obj; if (range == null) { return false; } return this == range; } /// /// Returns a value indicating whether a value is within a range. /// /// The value. /// Whether the value is within the range. public bool Contains(T value) { return ValueHelper.Compare(Minimum, value) <= 0 && ValueHelper.Compare(value, Maximum) <= 0; } /////// /////// Returns a new range that contains the value. /////// /////// The value to extend the range to. /////// The range which contains the value. ////public Range ExtendTo(T value) ////{ //// if (!HasData) //// { //// return new Range(value, value); //// } //// if (ValueHelper.Compare(Minimum, value) > 0) //// { //// return new Range(value, Maximum); //// } //// else if (ValueHelper.Compare(Maximum, value) < 0) //// { //// return new Range(Minimum, value); //// } //// return this; ////} /// /// Returns a value indicating whether two ranges intersect. /// /// The range to compare against this range. /// A value indicating whether the ranges intersect. public bool IntersectsWith(Range range) { if (!this.HasData || !range.HasData) { return false; } Func, Range, bool> rightCollidesWithLeft = (leftRange, rightRange) => (ValueHelper.Compare(rightRange.Minimum, leftRange.Maximum) <= 0 && ValueHelper.Compare(rightRange.Minimum, leftRange.Minimum) >= 0) || (ValueHelper.Compare(leftRange.Minimum, rightRange.Maximum) <= 0 && ValueHelper.Compare(leftRange.Minimum, rightRange.Minimum) >= 0); return rightCollidesWithLeft(this, range) || rightCollidesWithLeft(range, this); } /// /// Computes a hash code value. /// /// A hash code value. public override int GetHashCode() { if (!HasData) { return 0; } int num = 0x5374e861; num = (-1521134295 * num) + EqualityComparer.Default.GetHashCode(Minimum); return ((-1521134295 * num) + EqualityComparer.Default.GetHashCode(Maximum)); } /// /// Returns the string representation of the range. /// /// The string representation of the range. public override string ToString() { if (!this.HasData) { return Properties.Resources.Range_ToString_NoData; } else { return string.Format(CultureInfo.CurrentCulture, Properties.Resources.Range_ToString_Data, this.Minimum, this.Maximum); } } } } ================================================ FILE: WpfToolkit/DataVisualization/RangeEnumerableFunctions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Linq; namespace System.Windows.Controls.DataVisualization { /// /// Collection of functions that manipulate streams of ranges. /// internal static class RangeEnumerableExtensions { /// /// Returns the minimum and maximum values in a stream. /// /// The type of the stream. /// The stream. /// The range of values in the stream. public static Range GetRange(this IEnumerable that) where T : IComparable { IEnumerator enumerator = that.GetEnumerator(); if (!enumerator.MoveNext()) { return new Range(); } T minimum = enumerator.Current; T maximum = minimum; while (enumerator.MoveNext()) { T current = enumerator.Current; if (ValueHelper.Compare(minimum, current) == 1) { minimum = current; } if (ValueHelper.Compare(maximum, current) == -1) { maximum = current; } } return new Range(minimum, maximum); } /// /// Returns a range encompassing all ranges in a stream. /// /// The type of the minimum and maximum values. /// /// The stream. /// A range encompassing all ranges in a stream. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nesting necessary to provide method for use with all types of Range.")] public static Range Sum(this IEnumerable> that) where T : IComparable { Range sum = new Range(); IEnumerator> enumerator = that.GetEnumerator(); while (enumerator.MoveNext()) { sum = sum.Add(enumerator.Current); } return sum; } } } ================================================ FILE: WpfToolkit/DataVisualization/ReadOnlyObservableCollection.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.ObjectModel; namespace System.Windows.Controls.DataVisualization { /// /// An observable collection that can only be written to by internal /// classes. /// /// The type of object in the observable collection. /// internal class ReadOnlyObservableCollection : NoResetObservableCollection { /// /// Gets or sets a value indicating whether the owner is writing to the /// collection. /// private bool IsMutating { get; set; } /// /// A method that mutates the collection. /// /// The action to mutate the collection. public void Mutate(Action> action) { IsMutating = true; try { action(this); } finally { IsMutating = false; } } /// /// Removes an item from the collection at an index. /// /// The index to remove. protected override void RemoveItem(int index) { if (!IsMutating) { throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly); } else { base.RemoveItem(index); } } /// /// Sets an item at a particular location in the collection. /// /// The location to set an item. /// The item to set. protected override void SetItem(int index, T item) { if (!IsMutating) { throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly); } else { base.SetItem(index, item); } } /// /// Inserts an item in the collection. /// /// The index at which to insert the item. /// The item to insert. protected override void InsertItem(int index, T item) { if (!IsMutating) { throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly); } else { base.InsertItem(index, item); } } /// /// Clears the items from the collection. /// protected override void ClearItems() { if (!IsMutating) { throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly); } else { base.ClearItems(); } } } } ================================================ FILE: WpfToolkit/DataVisualization/ResourceDictionaryCollection.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.ObjectModel; namespace System.Windows.Controls.DataVisualization { /// /// Represents a collection of ResourceDictionary objects. /// /// Preview public partial class ResourceDictionaryCollection : Collection { /// /// Initializes a new instance of the ResourceDictionaryCollection class. /// public ResourceDictionaryCollection() { } } } ================================================ FILE: WpfToolkit/DataVisualization/StoryboardQueue.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Windows.Media.Animation; namespace System.Windows.Controls.DataVisualization { /// /// Represents a storyboard queue that plays storyboards in sequence. /// internal class StoryboardQueue { /// /// A queue of the storyboards. /// private Queue _storyBoards = new Queue(); /// /// Accepts a new storyboard to play in sequence. /// /// The storyboard to play. /// An action to execute when the /// storyboard completes. public void Enqueue(Storyboard storyBoard, EventHandler completedAction) { storyBoard.Completed += (sender, args) => { if (completedAction != null) { completedAction(sender, args); } _storyBoards.Dequeue(); Dequeue(); }; _storyBoards.Enqueue(storyBoard); if (_storyBoards.Count == 1) { Dequeue(); } } /// /// Removes the next storyboard in the queue and plays it. /// private void Dequeue() { if (_storyBoards.Count > 0) { Storyboard storyboard = _storyBoards.Peek(); storyboard.Begin(); } } } } ================================================ FILE: WpfToolkit/DataVisualization/StringFormatConverter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Globalization; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization { /// /// Converts a value to a string using a format string. /// public class StringFormatConverter : IValueConverter { /// /// Converts a value to a string by formatting it. /// /// The value to convert. /// The target type of the conversion. /// The format string. /// The culture to use for conversion. /// The formatted string. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) { return string.Empty; } return string.Format(CultureInfo.CurrentCulture, (parameter as string) ?? "{0}", value); } /// /// Converts a value from a string to a target type. /// /// The value to convert to a string. /// The target type of the conversion. /// A parameter used during the conversion /// process. /// The culture to use for the conversion. /// The converted object. public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } } } ================================================ FILE: WpfToolkit/DataVisualization/Themes/generic.xaml ================================================  ================================================ FILE: WpfToolkit/DataVisualization/Title/Title.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Windows; using System.Windows.Controls; namespace System.Windows.Controls.DataVisualization { /// /// Represents the title of a data visualization control. /// /// Preview public partial class Title : ContentControl { #if !SILVERLIGHT /// /// Initializes the static members of the Title class. /// static Title() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Title), new FrameworkPropertyMetadata(typeof(Title))); } #endif /// /// Initializes a new instance of the Title class. /// public Title() { #if SILVERLIGHT DefaultStyleKey = typeof(Title); #endif } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/BindingExtractor.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Windows.Data; namespace System.Windows.Controls.DataVisualization { /// /// Helper class which can extract the value from a source object using a binding path. It /// creates a Binding object based on the path, and calls SetBinding to a temporary /// FrameworkElement (base class) to extract the value. /// internal class BindingExtractor : FrameworkElement { #region public object Value /// /// Gets or sets a generic Value which will be the target of the binding. /// private object Value { get { return (object)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } /// /// Identifies the Value dependency property. /// private static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(object), typeof(BindingExtractor), null); #endregion /// /// Returns the value of the given Binding when applied on the given object instance. /// It does that by making a copy of the binding, setting its source to be the object /// instance and the target to be the member Value property. /// /// Object instance containing the property. /// Binding to the property to be retrieved. /// The value of the binding. public object RetrieveProperty(object instance, Binding valueBinding) { // We need to make a new instance each time because you can't change // the Source of a Binding once it has been set. Binding binding = new Binding() { BindsDirectlyToSource = valueBinding.BindsDirectlyToSource, Converter = valueBinding.Converter, ConverterCulture = valueBinding.ConverterCulture, ConverterParameter = valueBinding.ConverterParameter, Mode = valueBinding.Mode, NotifyOnValidationError = valueBinding.NotifyOnValidationError, Path = valueBinding.Path, Source = instance, UpdateSourceTrigger = valueBinding.UpdateSourceTrigger, ValidatesOnExceptions = valueBinding.ValidatesOnExceptions, #if !NO_COMPLETE_BINDING_PROPERTY_LIST FallbackValue = valueBinding.FallbackValue, StringFormat = valueBinding.StringFormat, TargetNullValue = valueBinding.TargetNullValue, ValidatesOnDataErrors = valueBinding.ValidatesOnDataErrors, #endif //#if !NO_VALIDATESONNOTIFYDATAERRORS // ValidatesOnNotifyDataErrors = valueBinding.ValidatesOnNotifyDataErrors, //#endif }; SetBinding(BindingExtractor.ValueProperty, binding); // Now this dependency property has been updated. return Value; } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Interpolators/DoubleInterpolator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Globalization; namespace System.Windows.Controls.DataVisualization { /// /// Interpolator which converts a numeric value from its [RangeMinimum, RangeMaximum] /// range to another value in the range [From, To]. /// /// Preview public class DoubleInterpolator : RangeInterpolator { /// /// Interpolates the given value between its [RangeMinimum, RangeMaximum] range /// and returns an interpolated value in the range [From, To]. /// /// Value to interpolate. /// An interpolated value in the range [From, To]. public override object Interpolate(double value) { double result = From; if (ActualDataMaximum - ActualDataMinimum != 0) { double ratio = (value - ActualDataMinimum) / (ActualDataMaximum - ActualDataMinimum); result = From + (ratio * (To - From)); } return result; } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Interpolators/HSLSolidColorBrushInterpolator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Windows.Controls.DataVisualization; using System.Windows.Media; namespace System.Windows.Controls.DataVisualization { /// /// Interpolator which converts a numeric value from its [ActualDataMinimum, ActualDataMaximum] /// range to a color in the range [From, To]. /// /// Experimental public class HSLSolidColorBrushInterpolator : RangeInterpolator { /// /// Interpolates the given value between its [ActualDataMinimum, ActualDataMaximum] range /// and returns a color in the range [From, To]. /// /// Value to interpolate. /// An interpolated color in the range [From, To]. public override object Interpolate(double value) { Color color = From; if (ActualDataMaximum - ActualDataMinimum != 0) { double ratio = (value - ActualDataMinimum) / (ActualDataMaximum - ActualDataMinimum); color = color.FromAhsl( (byte)((double)From.A + (ratio * (double)(To.A - From.A))), From.GetHue() + (ratio * (To.GetHue() - From.GetHue())), From.GetSaturation() + (ratio * (To.GetSaturation() - From.GetSaturation())), From.GetLightness() + (ratio * (To.GetLightness() - From.GetLightness()))); } return new SolidColorBrush(color); } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Interpolators/InterpolationMode.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization { /// /// Specifies the supported interpolation modes. /// /// Preview public enum InterpolationMode { /// /// Interpolation shall be applied to leaf nodes only in the tree. /// LeafNodesOnly = 0, /// /// Interpolation shall be applied to all nodes in the tree. /// AllNodes = 1, } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Interpolators/Interpolator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Globalization; using System.Windows.Data; namespace System.Windows.Controls.DataVisualization { /// /// Abstract base class for Interpolator converters. /// /// /// /// An Interpolator is used to project a value from a source range /// [ActualDataMinimum, ActualDataMaximum] to a target range [From, To]. /// The source range can be specified directly by setting the DataMinimum /// and/or DataMaximum properties, or indirectly by setting DataRangeBinding. /// When the DataRangeBinding property is set,the TreeMap will evaluate the /// binding for the entire tree, calculating the minimum and maximum values /// automatically. The custom target range and the actual interpolation /// logic is defined by sub-classes of this abstract class. /// /// /// Preview public abstract class Interpolator : FrameworkElement { /// /// Holds a helper object used to extract values using a property path. /// private BindingExtractor _helper; /// /// Gets or sets a value telling to which tree nodes the interpolation /// should be applied to. LeafNodesOnly by default. /// public InterpolationMode InterpolationMode { get; set; } /// /// Gets or sets a value representing the x:Name of the element to which /// the interpolated value will be applied. /// public string TargetName { get; set; } /// /// Gets or sets a value representing the path to a property which will /// receive the interpolated value. /// public string TargetProperty { get; set; } #region public double DataMinimum /// /// Gets or sets a value representing the fixed minimum value across the /// entire set. If the value is not set directly or is NaN, the /// ActualDataMaximum will be calculated automatically from the data set /// by using the DataRangeBinding property. /// public double DataMinimum { get { return (double)GetValue(DataMinimumProperty); } set { SetValue(DataMinimumProperty, value); } } /// /// Identifies the DataMinimum dependency property. /// public static readonly DependencyProperty DataMinimumProperty = DependencyProperty.Register( "DataMinimum", typeof(double), typeof(Interpolator), new PropertyMetadata(double.NaN)); #endregion public double DataMinimum #region public double DataMaximum /// /// Gets or sets a value representing the fixed maximum value across the /// entire set. If the value is not set directly or is NaN, the /// ActualDataMinimum will be calculated automatically from the data set /// by using the DataRangeBinding property. /// public double DataMaximum { get { return (double)GetValue(DataMaximumProperty); } set { SetValue(DataMaximumProperty, value); } } /// /// Identifies the DataMaximum dependency property. /// public static readonly DependencyProperty DataMaximumProperty = DependencyProperty.Register( "DataMaximum", typeof(double), typeof(Interpolator), new PropertyMetadata(double.NaN)); #endregion public double DataMaximum /// /// This fields contains the automatically calculated maximal value in /// the dataset. /// private double _actualDataMaximum; /// /// Gets the value representing the maximal value in the data set. It is /// automatically from the data set by using the DataRangeBinding /// property if DataMaximum is not set. If it is set, DataMaximum is /// returned. /// public double ActualDataMaximum { get { if (Double.IsNaN(DataMaximum)) { return _actualDataMaximum; } else { return DataMaximum; } } internal set { _actualDataMaximum = value; } } /// /// This fields contains the automatically calculated minimal value in /// the dataset. /// private double _actualDataMinimum; /// /// Gets the value representing the minimal value in the data set. It is /// automatically from the data set by using the DataRangeBinding /// property if DataMinimum is not set. If it is set, DataMinimum is /// returned. /// public double ActualDataMinimum { get { if (Double.IsNaN(DataMinimum)) { return _actualDataMinimum; } else { return DataMinimum; } } internal set { _actualDataMinimum = value; } } /// /// Gets or sets a binding to a property which will be examined to retrieve the minimum and maximum range /// values across the entire data set. If this value is null then the DataMinimum and DataMaximum values /// need be set manually. /// public Binding DataRangeBinding { get; set; } /// /// Initializes a new instance of the Interpolator class. /// protected Interpolator() { InterpolationMode = InterpolationMode.LeafNodesOnly; ActualDataMinimum = double.PositiveInfinity; ActualDataMaximum = double.NegativeInfinity; _helper = new BindingExtractor(); } /// /// If the DataRangeBinding property is set then this method updates the minimum/maximum range /// of this object by including the value passed in. /// /// Object to extract the value from (the Source of the DataRangeBinding). internal virtual void IncludeInRange(object data) { if (DataRangeBinding != null) { if (!Double.IsNaN(DataMinimum) && !Double.IsNaN(DataMaximum)) { return; } IConvertible input = _helper.RetrieveProperty(data, DataRangeBinding) as IConvertible; if (input == null) { throw new ArgumentException( Properties.Resources.Interpolator_IncludeInRange_DataRangeBindingNotIConvertible); } double value = input.ToDouble(CultureInfo.InvariantCulture); if (Double.IsNaN(DataMinimum) && value < ActualDataMinimum) { ActualDataMinimum = value; } if (Double.IsNaN(DataMaximum) && value > ActualDataMaximum) { ActualDataMaximum = value; } } } /// /// Called to interpolate the value of the given object between the DataMinimum and DataMaximum /// extremes, and to project it in a specific [From, To] range defined. The target range (and /// therefore the implementation of this method) is defined in a specific sub-class. /// /// Value to interpolate. /// An interpolated value. public abstract object Interpolate(double value); } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Interpolators/RangeInterpolator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization { /// /// Abstract class representing an interpolator which projects values to /// a continuous range defined by the From and To properties. /// /// The data type of the values in the target range. /// Preview public abstract class RangeInterpolator : Interpolator { /// /// Gets or sets a value representing the start value of the target range. /// public T From { get; set; } /// /// Gets or sets a value representing the end value of the target range. /// public T To { get; set; } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Interpolators/SolidColorBrushInterpolator.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Windows.Media; namespace System.Windows.Controls.DataVisualization { /// /// Interpolator which converts a numeric value from its [RangeMinimum, RangeMaximum] /// range to a color in the range [From, To]. /// /// Preview public class SolidColorBrushInterpolator : RangeInterpolator { /// /// Interpolates the given value between its [RangeMinimum, RangeMaximum] range /// and returns a color in the range [From, To]. /// /// Value to interpolate. /// An interpolated color in the range [From, To]. public override object Interpolate(double value) { Color color = From; if (ActualDataMaximum - ActualDataMinimum != 0) { double ratio = (value - ActualDataMinimum) / (ActualDataMaximum - ActualDataMinimum); color = Color.FromArgb( (byte)(From.A + (ratio * (To.A - From.A))), (byte)(From.R + (ratio * (To.R - From.R))), (byte)(From.G + (ratio * (To.G - From.G))), (byte)(From.B + (ratio * (To.B - From.B)))); } return new SolidColorBrush(color); } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Layout/SquaringAlgorithm.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Linq; namespace System.Windows.Controls.DataVisualization { /// /// Class encapsulating the logic of sub-dividing a parent rectangular area into child rectangles. /// It implements the squaring tree map algorithm where all child nodes are allocated /// areas proportional to their values, but the aspect ratio of each rectangle is kept /// as close as possible to a square. /// internal class SquaringAlgorithm { /// /// Holds the list of nodes being considered by the algorithm. /// private IList _areas; /// /// The current rectangle being divided. /// private Rect _currentRectangle; /// /// Internal index in the list of nodes being divided. /// private int _currentStart; /// /// Temporary variable used during the algorithm. Represents the ratio between /// the real area of the rectangle and the virtual value associated with the node. /// private double _factor; /// /// Subdivides the parent rectangle using squaring tree map algorithm into /// rectangles with areas specified by the children. The areas must add up /// to at most the area of the rectangle. /// /// Total area being split. /// The node associated with the total area. The /// children of this node will be allocated small chunks of the parent rectangle. /// How much of a gap should be left between the parent rectangle and the children. /// A list of RectangularArea objects describing areas associated with each of the children of parentNode. public IEnumerable> Split(Rect parentRectangle, TreeMapNode parentNode, Thickness margin) { IEnumerable> retVal; double area = parentNode.Area; if (parentNode.Children == null || parentNode.Children.Count() == 0 || area == 0) { retVal = Enumerable.Empty>(); } else { if (parentRectangle.Width - margin.Left - margin.Right <= 0 || parentRectangle.Height - margin.Top - margin.Bottom <= 0) { // Margins too big, no more room for children. Returning // zero sized rectangles for all children. retVal = from child in parentNode.Children select new Tuple(new Rect(0, 0, 0, 0), child); } else { // Leave as much room as specified by the margin _currentRectangle = new Rect( parentRectangle.X + margin.Left, parentRectangle.Y + margin.Top, parentRectangle.Width - margin.Left - margin.Right, parentRectangle.Height - margin.Top - margin.Bottom); _areas = (from child in parentNode.Children where child.Area != 0 orderby child.Area descending select child).ToArray(); // Factor is only computed once and used during the algorithm _factor = _currentRectangle.Width * _currentRectangle.Height / area; retVal = BuildTreeMap().ToArray(); } } return retVal; } /// /// This function returns an IEnumerable over the rectangles associated with the children, /// as divided using the tree map algorithm. /// /// A list of RectangularArea objects describing areas associated with each of the children. private IEnumerable> BuildTreeMap() { _currentStart = 0; while (_currentStart < _areas.Count) { foreach (Tuple rectangle in BuildTreeMapStep()) { yield return rectangle; } } } /// /// Performs one step of the body of the squaring tree map algorithm. /// /// List of rectangles calculated by this step. private IEnumerable> BuildTreeMapStep() { int last = _currentStart; double total = 0; double prevAspect = double.PositiveInfinity; double wh = 0; bool horizontal = _currentRectangle.Width > _currentRectangle.Height; for (; last < _areas.Count; last++) { total += GetArea(last); wh = total / (horizontal ? _currentRectangle.Height : _currentRectangle.Width); double curAspect = Math.Max(GetAspect(_currentStart, wh), GetAspect(last, wh)); if (curAspect > prevAspect) { total -= GetArea(last); wh = total / (horizontal ? _currentRectangle.Height : _currentRectangle.Width); last--; break; } prevAspect = curAspect; } if (last == _areas.Count) { last--; } double x = _currentRectangle.Left; double y = _currentRectangle.Top; for (int i = _currentStart; i <= last; i++) { if (horizontal) { double h = GetArea(i) / wh; Rect rect = new Rect(x, y, wh, h); yield return new Tuple(rect, _areas[i]); y += h; } else { double w = GetArea(i) / wh; Rect rect = new Rect(x, y, w, wh); yield return new Tuple(rect, _areas[i]); x += w; } } _currentStart = last + 1; if (horizontal) { _currentRectangle = new Rect(_currentRectangle.Left + wh, _currentRectangle.Top, Math.Max(0, _currentRectangle.Width - wh), _currentRectangle.Height); } else { _currentRectangle = new Rect(_currentRectangle.Left, _currentRectangle.Top + wh, _currentRectangle.Width, Math.Max(0, _currentRectangle.Height - wh)); } } /// /// Returns the calculated area of the node at the given index. /// /// Index of the node to consider. /// Area of the node, calculated based on the node's value multiplied by the current factor. private double GetArea(int i) { return _areas[i].Area * _factor; } /// /// Returns the aspect ratio of the area associated with the node at the given index. /// /// Index of the node to consider. /// Width of the area. /// Positive supra-unitary ratio of the given area. private double GetAspect(int i, double wh) { double aspect = GetArea(i) / (wh * wh); if (aspect < 1) { aspect = 1.0 / aspect; } return aspect; } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/Layout/TreeMapNode.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.DataVisualization { /// /// Holds the information needed by the tree map layout algorithm, such as the area /// associated with this node and the list of children. The class also contains /// an DataContext object which is the real user context, and a reference to the UI /// container associated with this node. /// internal class TreeMapNode { /// /// Gets or sets a value representing the area associated with this node. /// This value is relative to all the other values in the hierarchy; the layout /// algorithm will allocated a real area proportional to this value. /// public double Area { get; set; } /// /// Gets or sets the list of children under this node. /// public IEnumerable Children { get; set; } /// /// Gets or sets a value representing the WeakEventListener associated with the /// ItemsSource that created the children of this node. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by implementation; warning is only for the test project.")] internal WeakEventListener WeakEventListener { get; set; } /// /// Gets or sets a value representing a reference to the user's custom data object. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by implementation; warning is only for the test project.")] public object DataContext { get; set; } /// /// Gets or sets a value representing the associated Silverlight UI element. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by implementation; warning is only for the test project.")] public FrameworkElement Element { get; set; } /// /// Gets or sets a value representing the TreeMapItemDefinition used to describe /// properties of this item. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by implementation; warning is only for the test project.")] public TreeMapItemDefinition ItemDefinition { get; set; } /// /// Gets or sets a value representing the padding between this node and its children. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by implementation; warning is only for the test project.")] public Thickness ChildItemPadding { get; set; } /// /// Gets or sets a value representing the level of this node in the tree (the /// root node is at level 0). /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by implementation; warning is only for the test project.")] public int Level { get; set; } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/TreeMap.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Windows.Data; using System.Windows.Markup; namespace System.Windows.Controls.DataVisualization { /// /// Represents a control which can display hierarchical data as a set of nested rectangles. /// Each item in the hierarchy is laid out in a rectangular area of a size proportional to /// the value associated with the item. /// /// /// You populate a TreeMap by setting its ItemsSource property to the root of the hierarchy /// you would like to display. The ItemDefinition property must be set to an instance of a /// TreeMapItemDefinition with appropriate bindings for Value (identifying the value to be used /// when calculating relative item sizes) and ItemsSource (identifying the collection of /// children for each item). /// /// Preview [TemplatePart(Name = ContainerName, Type = typeof(Canvas))] [ContentProperty("ItemsSource")] public class TreeMap : Control { /// /// The name of the Container template part. /// private const string ContainerName = "Container"; #region private object InterpolatorValue /// /// Identifies the InterpolatorValue dependency property. /// private static readonly DependencyProperty InterpolatorValueProperty = DependencyProperty.Register( "InterpolatorValue", typeof(object), typeof(TreeMap), null); /// /// Gets or sets a generic value used as a temporary storage used as a source for TargetName/TargetProperty binding. /// private object InterpolatorValue { get { return (object)GetValue(InterpolatorValueProperty); } set { SetValue(InterpolatorValueProperty, value); } } #endregion /// /// Holds a helper object used to extract values using a property path. /// private BindingExtractor _helper; /// /// The roots of the pre-calculated parallel tree of TreeMapNodes. /// private IEnumerable _nodeRoots; /// /// Cached sequence of all TreeMapNodes used by GetTreeMapNodes. /// private IEnumerable _getTreeMapNodesCache; #region public TreeMapItemDefinitionSelector TreeMapItemDefinitionSelector /// /// Gets or sets the selector used to choose the item template dynamically. /// public TreeMapItemDefinitionSelector ItemDefinitionSelector { get { return (TreeMapItemDefinitionSelector)GetValue(ItemDefinitionSelectorProperty); } set { SetValue(ItemDefinitionSelectorProperty, value); } } /// /// Identifies the ItemDefinitionSelector dependency property. /// public static readonly DependencyProperty ItemDefinitionSelectorProperty = DependencyProperty.Register( "ItemDefinitionSelector", typeof(TreeMapItemDefinitionSelector), typeof(TreeMap), new PropertyMetadata(OnItemDefinitionSelectorPropertyChanged)); /// /// Called when the value of the TreeMapItemDefinitionSelectorProperty property changes. /// /// Reference to the TreeMap object. /// Event handler arguments. private static void OnItemDefinitionSelectorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TreeMap treeMap = d as TreeMap; if (treeMap != null) { TreeMapItemDefinitionSelector oldValue = e.OldValue as TreeMapItemDefinitionSelector; TreeMapItemDefinitionSelector newValue = e.NewValue as TreeMapItemDefinitionSelector; treeMap.OnItemDefinitionSelectorPropertyChanged(oldValue, newValue); } } /// /// Called when the value of the ItemDefinitionSelectorProperty property changes. /// Triggers a recalculation of the layout. /// /// The old selector. /// The new selector. protected virtual void OnItemDefinitionSelectorPropertyChanged(TreeMapItemDefinitionSelector oldValue, TreeMapItemDefinitionSelector newValue) { RebuildTree(); } #endregion #region public TreeMapItemDefinition ItemDefinition /// /// Gets or sets a value representing the template used to display each item. /// public TreeMapItemDefinition ItemDefinition { get { return (TreeMapItemDefinition)GetValue(ItemDefinitionProperty); } set { SetValue(ItemDefinitionProperty, value); } } /// /// Identifies the ItemDefinition dependency property. /// public static readonly DependencyProperty ItemDefinitionProperty = DependencyProperty.Register( "ItemDefinition", typeof(TreeMapItemDefinition), typeof(TreeMap), new PropertyMetadata(OnItemDefinitionPropertyChanged)); /// /// Called when the value of the ItemDefinitionProperty property changes. /// /// Reference to the TreeMap object. /// Event handler arguments. private static void OnItemDefinitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TreeMap treeMap = d as TreeMap; if (treeMap != null) { TreeMapItemDefinition oldValue = e.OldValue as TreeMapItemDefinition; TreeMapItemDefinition newValue = e.NewValue as TreeMapItemDefinition; // Unregister old TreeMapItemDefinition if (oldValue != null) { oldValue.PropertyChanged -= treeMap.OnItemDefinitionPropertyChanged; } // Register new TreeMapItemDefinition if (newValue != null) { newValue.PropertyChanged += treeMap.OnItemDefinitionPropertyChanged; } treeMap.OnItemDefinitionPropertyChanged(oldValue, newValue); } } /// /// This callback ensures that any change in TreeMapItemDefinition. /// /// Source TreeMapItemDefinition object. /// Event handler arguments (parameter name). private void OnItemDefinitionPropertyChanged(object sender, PropertyChangedEventArgs e) { RebuildTree(); } /// /// Called when the value of the ItemDefinitionProperty property changes. /// Triggers a recalculation of the layout. /// /// The old item definition. /// The new item definition. protected virtual void OnItemDefinitionPropertyChanged(TreeMapItemDefinition oldValue, TreeMapItemDefinition newValue) { RebuildTree(); } #endregion #region public IEnumerable ItemsSource /// /// Gets or sets a value representing the list of hierarchies used to generate /// content for the TreeMap. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( "ItemsSource", typeof(IEnumerable), typeof(TreeMap), new PropertyMetadata(OnItemsSourcePropertyChanged)); /// /// Called when the value of the ItemsSourceProperty property changes. /// /// Reference to the TreeMap object. /// Event handler arguments. private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TreeMap treeMap = d as TreeMap; if (treeMap != null) { IEnumerable oldValue = e.OldValue as IEnumerable; IEnumerable newValue = e.NewValue as IEnumerable; treeMap.OnItemsSourcePropertyChanged(oldValue, newValue); } } /// /// Called when the value of the ItemsSourceProperty property changes. /// /// The old ItemsSource collection. /// The new ItemsSource collection. protected virtual void OnItemsSourcePropertyChanged(IEnumerable oldValue, IEnumerable newValue) { // Remove handler for oldValue.CollectionChanged (if present) INotifyCollectionChanged oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged; if (null != oldValueINotifyCollectionChanged) { // Detach the WeakEventListener if (null != _weakEventListener) { _weakEventListener.Detach(); _weakEventListener = null; } } // Add handler for newValue.CollectionChanged (if possible) INotifyCollectionChanged newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged; if (null != newValueINotifyCollectionChanged) { // Use a WeakEventListener so that the backwards reference doesn't keep this object alive _weakEventListener = new WeakEventListener(this); _weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs); _weakEventListener.OnDetachAction = (weakEventListener) => newValueINotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent; newValueINotifyCollectionChanged.CollectionChanged += _weakEventListener.OnEvent; } // Handle property change RebuildTree(); } /// /// WeakEventListener used to handle INotifyCollectionChanged events. /// private WeakEventListener _weakEventListener; /// /// Method that handles the ObservableCollection.CollectionChanged event for the ItemsSource property. /// /// The object that raised the event. /// The event data. private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { RebuildTree(); } #endregion #region private Collection Interpolators /// /// Gets or sets a value representing a collection of interpolators to use in TreeMap. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Setter is public to work around a limitation with the XAML editing tools.")] public Collection Interpolators { get { return (Collection)GetValue(InterpolatorsProperty); } set { throw new NotSupportedException(Properties.Resources.TreeMap_Interpolators_SetterNotSupported); } } /// /// Identifies the Interpolators dependency property. /// public static readonly DependencyProperty InterpolatorsProperty = DependencyProperty.Register( "Interpolators", typeof(Collection), typeof(TreeMap), new PropertyMetadata(OnInterpolatorsPropertyChanged)); /// /// Called when the value of the InterpolatorsProperty property changes. /// /// Reference to the TreeMap object. /// Event handler arguments. private static void OnInterpolatorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TreeMap treeMap = d as TreeMap; if (treeMap != null) { Collection oldValue = e.OldValue as Collection; Collection newValue = e.NewValue as Collection; treeMap.OnInterpolatorsPropertyChanged(oldValue, newValue); } } /// /// Called when the value of the InterpolatorsProperty property changes. /// Triggers a recalculation of the layout. /// /// The old Interpolators collection. /// The new Interpolators collection. protected virtual void OnInterpolatorsPropertyChanged(Collection oldValue, Collection newValue) { RebuildTree(); } #endregion #region Template Parts /// /// The Container template part is used to hold all the items inside /// a TreeMap. /// private Canvas _containerElement; /// /// Gets the Container template part that is used to hold all the items inside /// a TreeMap. /// internal Canvas ContainerElement { get { return _containerElement; } private set { // Detach from the old Container element if (_containerElement != null) { _containerElement.Children.Clear(); } // Attach to the new Container element _containerElement = value; } } #endregion /// /// Initializes a new instance of the TreeMap class. /// public TreeMap() { _helper = new BindingExtractor(); DefaultStyleKey = typeof(TreeMap); SetValue(InterpolatorsProperty, new ObservableCollection()); (Interpolators as ObservableCollection).CollectionChanged += new NotifyCollectionChangedEventHandler(OnInterpolatorsCollectionChanged); } /// /// Invoked whenever application code or internal processes call ApplyTemplate. Gets references /// to the template parts required by this control. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); ContainerElement = GetTemplateChild(ContainerName) as Canvas; RebuildTree(); } /// /// Constructs a new instance of an element used to display an item in the tree. /// /// /// By default TreeMap will use the template set in its ItemDefinition property, or the value /// returned from GetTemplateForItemOverride if overridden. Override this method to build a /// custom element. /// /// One of the items in the ItemsSource hierarchy. /// The level of the item in the hierarchy. /// A new FrameworkElement which will be added to the TreeMap control. If this /// method returns null the TreeMap will create the item using the ItemDefinition property, /// or the value returned by TreeMapItemDefinitionSelector if specified. protected virtual FrameworkElement GetContainerForItemOverride(object data, int level) { return null; } /// /// Performs the Arrange pass of the layout. /// /// /// We round rectangles to snap to nearest pixels. We do that to avoid /// anti-aliasing which results in better appearance. Moreover to get /// correct layout we would need to use UseLayoutRounding=false which /// is Silverlight specific. A side effect is that areas for rectangles /// in the visual tree no longer can be used to compare them as dimensions /// are not rounded and therefore not precise. /// /// The final area within the parent that this element should use to arrange itself and its children. /// The actual size used. protected override Size ArrangeOverride(Size finalSize) { // Sets ActualHeight & ActualWidth for the container finalSize = base.ArrangeOverride(finalSize); if (_nodeRoots != null && ContainerElement != null) { // Create a temporary pseudo-root node containing all the top-level nodes TreeMapNode root = new TreeMapNode() { Area = _nodeRoots.Sum(x => x.Area), Children = _nodeRoots, ChildItemPadding = new Thickness(0) }; // Calculate associated rectangles. We use ContainerElement, // not finalSize so all elements that are above it like border // (with padding and border) are taken into account IEnumerable> measuredRectangles = ComputeRectangles( root, new Rect(0, 0, ContainerElement.ActualWidth, ContainerElement.ActualHeight)); // Position everything foreach (Tuple rectangle in measuredRectangles) { FrameworkElement element = rectangle.Item2.Element; if (element != null) { double roundedTop = Math.Round(rectangle.Item1.Top); double roundedLeft = Math.Round(rectangle.Item1.Left); double height = Math.Round(rectangle.Item1.Height + rectangle.Item1.Top) - roundedTop; double width = Math.Round(rectangle.Item1.Width + rectangle.Item1.Left) - roundedLeft; // Fully specify element location/size (setting size is required on WPF) Canvas.SetLeft(element, roundedLeft); Canvas.SetTop(element, roundedTop); element.Width = width; element.Height = height; element.Arrange(new Rect(roundedLeft, roundedTop, width, height)); } } } return finalSize; } /// /// Triggers a recalculation of the layout when items are added/removed from the Interpolators collection. /// /// Reference to the Interpolators collection. /// Event handler arguments. private void OnInterpolatorsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { RebuildTree(); } /// /// Returns a sequence of TreeMapNodes in breadth-first order. /// /// Sequence of TreeMapNodes. private IEnumerable GetTreeMapNodes() { if (_getTreeMapNodesCache == null) { // Create a new list List allNodes = new List(); // Seed the queue with the roots Queue nodes = new Queue(); foreach (TreeMapNode node in _nodeRoots ?? Enumerable.Empty()) { nodes.Enqueue(node); } // Process the queue in breadth-first order while (0 < nodes.Count) { TreeMapNode node = nodes.Dequeue(); allNodes.Add(node); foreach (TreeMapNode child in node.Children) { nodes.Enqueue(child); } } // Cache the list _getTreeMapNodesCache = allNodes; } // Return the cached sequence return _getTreeMapNodesCache; } /// /// Recursively computes TreeMap rectangles given the root node and the bounding rectangle as start. /// /// Root of the TreeMapNode tree. /// Bounding rectangle which will be sub-divided. /// A list of RectangularAreas containing a rectangle for each node in the tree. private IEnumerable> ComputeRectangles(TreeMapNode root, Rect boundingRectangle) { Queue> treeQueue = new Queue>(); treeQueue.Enqueue(new Tuple(boundingRectangle, root)); // Perform a breadth-first traversal of the tree SquaringAlgorithm algorithm = new SquaringAlgorithm(); while (treeQueue.Count > 0) { Tuple currentParent = treeQueue.Dequeue(); yield return currentParent; foreach (Tuple rectangle in algorithm.Split(currentParent.Item1, currentParent.Item2, currentParent.Item2.ChildItemPadding)) { treeQueue.Enqueue(rectangle); } } } /// /// Builds the parallel trees of TreeMapNodes with references to the original user's trees. /// /// The list of roots of the user hierarchies (whatever was passed through ItemsSource). /// Level being processed at this recursive call (the root node is at level 0). /// The list of roots of the internal trees of TreeMapNodes. private IEnumerable BuildTreeMapTree(IEnumerable nodes, int level) { List retList = new List(); if (nodes == null) { return retList; } foreach (object root in nodes) { // Give the template selector a chance to override the template for this item. TreeMapItemDefinition template = null; if (ItemDefinitionSelector != null) { template = ItemDefinitionSelector.SelectItemDefinition(this, root, level); } // Use the default otherwise if (template == null) { template = ItemDefinition; } if (template == null) { throw new ArgumentException( Properties.Resources.TreeMap_BuildTreeMapTree_TemplateNotSet); } // Silently create 0 elements if ValueBinding is set to null // in the template if (template.ValueBinding != null) { IEnumerable objectChildren = (template.ItemsSource != null) ? _helper.RetrieveProperty(root, template.ItemsSource) as IEnumerable : null; IEnumerable children = (objectChildren != null) ? BuildTreeMapTree(objectChildren, level + 1) : children = Enumerable.Empty(); // Subscribe to CollectionChanged for the collection WeakEventListener weakEventListener = null; INotifyCollectionChanged objectChildrenINotifyCollectionChanged = objectChildren as INotifyCollectionChanged; if (objectChildrenINotifyCollectionChanged != null) { // Use a WeakEventListener so that the backwards reference doesn't keep this object alive weakEventListener = new WeakEventListener(this); weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs); weakEventListener.OnDetachAction = (wel) => objectChildrenINotifyCollectionChanged.CollectionChanged -= wel.OnEvent; objectChildrenINotifyCollectionChanged.CollectionChanged += weakEventListener.OnEvent; } // Auto-aggregate children area values double area; if (children.Any()) { area = children.Sum(x => x.Area); } else { IConvertible value = _helper.RetrieveProperty(root, template.ValueBinding) as IConvertible; if (value == null) { // Provide a default value so there's something to display value = 1.0; } area = value.ToDouble(CultureInfo.InvariantCulture); } // Do not include elements with negative or 0 size in the // VisualTransition tree. We skip interpolation for such // elements as well if (area > 0) { // Calculate ranges for all interpolators, only consider leaf // nodes in the LeafNodesOnly mode, or all nodes in the AllNodes // mode. foreach (Interpolator interpolator in Interpolators) { if (interpolator.InterpolationMode == InterpolationMode.AllNodes || !children.Any()) { interpolator.IncludeInRange(root); } } retList.Add(new TreeMapNode() { DataContext = root, Level = level, Area = area, ItemDefinition = template, ChildItemPadding = template.ChildItemPadding, Children = children, WeakEventListener = weakEventListener, }); } } } return retList; } /// /// Extracts all children from the user's trees (ItemsSource) into a flat list, and /// creates UI elements for them. /// private void CreateChildren() { // Breadth-first traversal so elements closer to the root will be added first, // so that leaf elements will show on top of them. foreach (TreeMapNode current in GetTreeMapNodes()) { // Create the UI element and keep a reference to it in our tree FrameworkElement element = GetContainerForItemOverride(current.DataContext, current.Level); if (element == null && current.ItemDefinition.ItemTemplate != null) { element = current.ItemDefinition.ItemTemplate.LoadContent() as FrameworkElement; } // If an element was created if (element != null) { current.Element = element; // Apply interpolators to element foreach (Interpolator interpolator in Interpolators) { // Apply interpolators only for leaf nodes in the // LeafNodesOnly mode, or for all nodes in the AllNodes // mode. if (interpolator.InterpolationMode == InterpolationMode.AllNodes || !current.Children.Any()) { DependencyObject target = element.FindName(interpolator.TargetName) as DependencyObject; if (target != null) { SetBinding( InterpolatorValueProperty, new Binding(interpolator.TargetProperty) { Source = target, Mode = BindingMode.TwoWay }); if (interpolator.DataRangeBinding == null) { throw new ArgumentException( Properties.Resources.TreeMap_CreateChildren_InterpolatorBindingNotSet); } // Extract the current value to interpolate IConvertible value = _helper.RetrieveProperty(current.DataContext, interpolator.DataRangeBinding) as IConvertible; if (value == null) { throw new ArgumentException( Properties.Resources.Interpolator_IncludeInRange_DataRangeBindingNotIConvertible); } // This will update the TargetProperty of the TargetName object InterpolatorValue = interpolator.Interpolate(value.ToDouble(CultureInfo.InvariantCulture)); } } } // Add new child to the panel element.DataContext = current.DataContext; ContainerElement.Children.Add(element); } } } /// /// Called internally whenever a property of TreeMap is changed and the internal /// structures need to be rebuilt in order to recalculate the layout. /// private void RebuildTree() { if (ContainerElement != null) { // Unhook from CollectionChanged foreach (TreeMapNode treeMapNode in GetTreeMapNodes().Where(n => n.WeakEventListener != null)) { treeMapNode.WeakEventListener.Detach(); } // Reset all interpolators foreach (Interpolator interpolator in Interpolators) { interpolator.ActualDataMinimum = double.PositiveInfinity; interpolator.ActualDataMaximum = double.NegativeInfinity; interpolator.DataContext = this.DataContext; } // Build the parallel tree of TreeMapNodes needed by the algorithm _nodeRoots = BuildTreeMapTree(ItemsSource, 0); // Clear cache _getTreeMapNodesCache = null; // Populate the TreeMap panel with a flat list of all children // in the hierarchy passed in. ContainerElement.Children.Clear(); CreateChildren(); // Refresh UI InvalidateArrange(); } } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/TreeMapItemDefinition.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.ComponentModel; using System.Windows.Data; using System.Windows.Markup; namespace System.Windows.Controls.DataVisualization { /// /// Represents a class that defines various aspects of TreeMap items. /// /// Preview [ContentProperty("ItemTemplate")] public class TreeMapItemDefinition : INotifyPropertyChanged { /// /// A value representing the DataTemplate to instantiate in /// order to create a representation of each TreeMap item. /// private DataTemplate _itemTemplate; /// /// Gets or sets a value representing the DataTemplate to instantiate in /// order to create a representation of each TreeMap item. /// public DataTemplate ItemTemplate { get { return _itemTemplate; } set { if (value != _itemTemplate) { _itemTemplate = value; NotifyPropertyChanged("ItemTemplate"); } } } /// /// A value representing a binding which can be used /// to retrieve the value associated with each item, needed to calculate /// relative areas of TreeMap items. /// private Binding _valueBinding; /// /// Gets or sets a value representing a binding which can be used /// to retrieve the value associated with each item, needed to calculate /// relative areas of TreeMap items. /// public Binding ValueBinding { get { return _valueBinding; } set { if (value != _valueBinding) { _valueBinding = value; NotifyPropertyChanged("ValueBinding"); } } } /// /// Gets or sets the Value Path used to set ValueBinding for retrieving /// the value associated with each item, needed to calculate relative /// areas of TreeMap items. /// public string ValuePath { get { return (null != ValueBinding) ? ValueBinding.Path.Path : null; } set { if (value != ValuePath) { if (null == value) { ValueBinding = null; } else { ValueBinding = new Binding(value); } // PropertyChanged(); thru ValueBinding } } } /// /// The binding that indicates where to find the collection /// that represents the next level in the data hierarchy. /// private Binding _itemsSource; /// /// Gets or sets the binding that indicates where to find the collection /// that represents the next level in the data hierarchy. /// public Binding ItemsSource { get { return _itemsSource; } set { if (value != _itemsSource) { _itemsSource = value; NotifyPropertyChanged("ItemsSource"); } } } /// /// A property representing the amount of space to leave /// between a parent item and its children. /// private Thickness _childItemPadding; /// /// Gets or sets a property representing the amount of space to leave /// between a parent item and its children. /// public Thickness ChildItemPadding { get { return _childItemPadding; } set { if (value != _childItemPadding) { _childItemPadding = value; NotifyPropertyChanged("ChildItemPadding"); } } } /// /// Initializes a new instance of the TreeMapItemDefinition class. /// public TreeMapItemDefinition() { ChildItemPadding = new Thickness(0); } /// /// PropertyChanged event required by INotifyPropertyChanged. /// public event PropertyChangedEventHandler PropertyChanged; /// /// Updates the TreeMap if one of properties changes. /// /// The parameter name. protected void NotifyPropertyChanged(string parameterName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(parameterName)); } } } } ================================================ FILE: WpfToolkit/DataVisualization/TreeMap/TreeMapItemDefinitionSelector.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.ObjectModel; using System.Windows.Markup; namespace System.Windows.Controls.DataVisualization { /// /// Provides a way to choose a TreeMapItemDefinition based on the data item and /// the level of the item in the tree. /// /// Preview public abstract class TreeMapItemDefinitionSelector { /// /// Initializes a new instance of the TreeMapItemDefinitionSelector class. /// protected TreeMapItemDefinitionSelector() { } /// /// Returns an instance of a TreeMapItemDefinition class used to specify properties for the /// current item. /// /// Reference to the TreeMap class. /// One of the nodes in the ItemsSource hierarchy. /// The level of the node in the hierarchy. /// The TreeMapItemDefinition to be used for this node. If this method returns null /// the TreeMap will use the value of its ItemDefinition property. public abstract TreeMapItemDefinition SelectItemDefinition(TreeMap treeMap, object item, int level); } } ================================================ FILE: WpfToolkit/DataVisualization/Tuple.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization { /// /// Represents a 2-tuple, or pair. /// /// The type of the tuple's first component. /// The type of the tuple's second component. internal class Tuple { /// /// Gets the value of the current Tuple(T1, T2) object's first component. /// public T1 Item1 { get; private set; } /// /// Gets the value of the current Tuple(T1, T2) object's second component. /// public T2 Item2 { get; private set; } /// /// Initializes a new instance of the Tuple(T1, T2) class. /// /// The value of the tuple's first component. /// The value of the tuple's second component. public Tuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; } } } ================================================ FILE: WpfToolkit/DataVisualization/UniqueObservableCollection.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Collections.ObjectModel; namespace System.Windows.Controls.DataVisualization { /// /// An observable collection that does not allow duplicates. /// /// The type of items in the collection. internal class UniqueObservableCollection : ObservableCollection { /// /// Inserts an item at an index. Throws if the item already exists in the collection. /// /// The index at which to insert the item. /// The item to insert. protected override void InsertItem(int index, T item) { if (!this.Contains(item)) { base.InsertItem(index, item); } else { throw new InvalidOperationException(Properties.Resources.UniqueObservableCollection_InvalidAttemptToInsertADuplicateItem); } } /// /// Sets an item at a given index. Throws if the item already exists at another index. /// /// The index at which to insert the item. /// The item to be inserted. protected override void SetItem(int index, T item) { int newItemIndex = this.IndexOf(item); if (newItemIndex != -1 && newItemIndex != index) { throw new InvalidOperationException(Properties.Resources.UniqueObservableCollection_InvalidAttemptToInsertADuplicateItem); } else { base.SetItem(index, item); } } /// /// Clears all items in the collection by removing them individually. /// protected override void ClearItems() { IList items = new List(this); foreach (T item in items) { Remove(item); } } } } ================================================ FILE: WpfToolkit/DataVisualization/Unit.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. namespace System.Windows.Controls.DataVisualization { /// /// Units of measure. /// public enum Unit { /// /// The corresponding value is in pixels. /// Pixels, /// /// The corresponding value is in degrees. /// Degrees, } } ================================================ FILE: WpfToolkit/DataVisualization/UnitValue.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; namespace System.Windows.Controls.DataVisualization { /// /// A value in units. /// public struct UnitValue : IComparable { /// /// Returns a UnitValue representing an invalid value. /// /// UnitValue instance. public static UnitValue NaN() { return new UnitValue { Value = double.NaN }; } /// /// Instantiates a new instance of the UnitValue struct. /// /// The value associated with the units. /// The units associated with the value. public UnitValue(double value, Unit unit) : this() { Value = value; Unit = unit; } /// /// Gets the value associated with the units. /// public double Value { get; private set; } /// /// Gets the units associated with the value. /// public Unit Unit { get; private set; } /// /// Compares two unit values to determine if they are equal or not. /// /// The object being compared. /// A number smaller than zero if the obj is larger than this /// object. A number equal to 0 if they are equal. A number greater /// than zero if this unit value is greater than obj. public int CompareTo(object obj) { UnitValue unitValue = (UnitValue) obj; if (unitValue.Unit != this.Unit) { throw new InvalidOperationException("Cannot compare two unit values with different units."); } return this.Value.CompareTo(unitValue.Value); } /// /// Determines if two values are equal. /// /// The other value. /// A value indicating whether values are equal. public override bool Equals(object obj) { if (!(obj is UnitValue)) { return false; } UnitValue unitValue = (UnitValue)obj; if ((Object.ReferenceEquals(unitValue.Value, this.Value) || Object.Equals(unitValue.Value, this.Value)) && unitValue.Unit == this.Unit) { return true; } return false; } /// /// Determines whether two unit value objects are equal. /// /// The left unit value. /// The right unit value. /// A value indicating whether two unit value objects are /// equal. public static bool operator ==(UnitValue left, UnitValue right) { return left.Equals(right); } /// /// Determines whether two unit value objects are not equal. /// /// The left unit value. /// The right unit value. /// A value indicating whether two unit value objects are not /// equal. public static bool operator !=(UnitValue left, UnitValue right) { return !left.Equals(right); } /// /// Determines whether the left value is smaller than the right. /// /// The left unit value. /// The right unit value. /// A value indicating whether the left value is smaller than /// the right. public static bool operator <(UnitValue left, UnitValue right) { return left.CompareTo(right) < 0; } /// /// Determines whether the left value is larger than the right. /// /// The left unit value. /// The right unit value. /// A value indicating whether the left value is larger than /// the right. public static bool operator >(UnitValue left, UnitValue right) { return left.CompareTo(right) > 0; } /// /// Returns the hash code of the unit value object. /// /// The hash code. public override int GetHashCode() { unchecked { return this.Value.GetHashCode() + (int)this.Unit; } } } } ================================================ FILE: WpfToolkit/DataVisualization/ValueHelper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Globalization; using System.Collections.Generic; using System.Linq; using System.Diagnostics; using System.Windows; namespace System.Windows.Controls.DataVisualization { /// /// A set of functions for data conversion operations. /// internal static class ValueHelper { /// /// The value of a single radian. /// public const double Radian = Math.PI / 180.0; /// /// Returns a value indicating whether this value can be graphed on a /// linear axis. /// /// The value to evaluate. /// A value indicating whether this value can be graphed on a /// linear axis. public static bool CanGraph(double value) { return !double.IsNaN(value) && !double.IsNegativeInfinity(value) && !double.IsPositiveInfinity(value) && !double.IsInfinity(value); } /// /// Attempts to convert an object into a double. /// /// The value to convert. /// The double value. /// A value indicating whether the value can be converted to a /// double. public static bool TryConvert(object value, out double doubleValue) { doubleValue = default(double); try { if (value != null && (value is double || value is int || value is byte || value is short || value is decimal || value is float || value is long || value is uint || value is sbyte || value is ushort || value is ulong)) { doubleValue = ValueHelper.ToDouble(value); return true; } } catch (FormatException) { } catch (InvalidCastException) { } return false; } /// /// Attempts to convert an object into a date time. /// /// The value to convert. /// The double value. /// A value indicating whether the value can be converted to a /// date time. public static bool TryConvert(object value, out DateTime dateTimeValue) { dateTimeValue = default(DateTime); if (value != null && value is DateTime) { dateTimeValue = (DateTime)value; return true; } return false; } /////// /////// Converts a value in an IComparable. /////// /////// The value to convert. /////// The converted value. ////public static IComparable ToComparable(object value) ////{ //// double doubleValue; //// DateTime dateTimeValue; //// if (TryConvert(value, out doubleValue)) //// { //// return doubleValue; //// } //// else if (TryConvert(value, out dateTimeValue)) //// { //// return dateTimeValue; //// } //// IComparable comparable = value as IComparable; //// return (comparable != null); ////} /// /// Converts an object into a double. /// /// The value to convert to a double. /// The converted double value. public static double ToDouble(object value) { return Convert.ToDouble(value, CultureInfo.InvariantCulture); } /// /// Converts a value to a date. /// /// The value to convert to a date. /// The converted date value. public static DateTime ToDateTime(object value) { return Convert.ToDateTime(value, CultureInfo.InvariantCulture); } /// /// Returns a sequence of date time values from a start and end date /// time inclusive. /// /// The start date time. /// The end date time. /// The number of values to return. /// A sequence of date time values. public static IEnumerable GetDateTimesBetweenInclusive(DateTime start, DateTime end, long count) { Debug.Assert(count >= 2L, "Count must be at least 2."); return GetIntervalsInclusive(start.Ticks, end.Ticks, count).Select(value => new DateTime(value)); } /// /// Returns a sequence of time span values within a time span inclusive. /// /// The time span to split. /// The number of time spans to return. /// A sequence of time spans. public static IEnumerable GetTimeSpanIntervalsInclusive(TimeSpan timeSpan, long count) { Debug.Assert(count >= 2L, "Count must be at least 2."); long distance = timeSpan.Ticks; return GetIntervalsInclusive(0, distance, count).Select(value => new TimeSpan(value)); } /// /// Returns that intervals between a start and end value, including those /// start and end values. /// /// The start value. /// The end value. /// The total number of intervals. /// A sequence of intervals. public static IEnumerable GetIntervalsInclusive(long start, long end, long count) { Debug.Assert(count >= 2L, "Count must be at least 2."); long interval = end - start; for (long index = 0; index < count; index++) { double ratio = (double)index / (double)(count - 1); long value = (long)((ratio * interval) + start); yield return value; } } /// /// Removes the noise from double math. /// /// The value. /// A double without a noise. internal static double RemoveNoiseFromDoubleMath(double value) { if (value == 0.0 || Math.Abs((Math.Log10(Math.Abs(value)))) < 27) { return (double)((decimal)value); } return Double.Parse(value.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); } /// /// Converts a range into a double range. /// /// The range to convert. /// A range with its members converted to doubles. public static Range ToDoubleRange(this Range range) { if (!range.HasData) { return new Range(); } else { return new Range((double)range.Minimum, (double)range.Maximum); } } /// /// Converts a range into a date time range. /// /// The range to convert. /// A range with its members converted to date times. /// public static Range ToDateTimeRange(this Range range) { if (!range.HasData) { return new Range(); } else { return new Range((DateTime)range.Minimum, (DateTime)range.Maximum); } } /////// /////// Returns the point given an angle and a distanceFromOrigin. /////// /////// The angle of orientation. /////// The radius. /////// The point calculated from the angle and radius. ////public static Point GetPoint(double angle, double distanceFromOrigin) ////{ //// return new Point(Math.Cos(angle * Radian) * distanceFromOrigin, Math.Sin(angle * Radian) * distanceFromOrigin); ////} /// /// Compares two IComparables returning -1 if the left is null and 1 if /// the right is null. /// /// The left comparable. /// The right comparable. /// A value indicating which is larger. public static int Compare(IComparable left, IComparable right) { if (left == null && right == null) { return 0; } else if (left == null && right != null) { return -1; } else if (left != null && right == null) { return 1; } else { return left.CompareTo(right); } } /// /// Applies the translate transform to a point. /// /// The origin point. /// The offset point. /// The translated point. public static Point Translate(this Point origin, Point offset) { return new Point(origin.X + offset.X, origin.Y + offset.Y); } /// /// Converts any range to a range of IComparable. /// /// The range to be converted. /// The new range type. public static Range ToComparableRange(this Range range) { if (range.HasData) { return new Range(range.Minimum, range.Maximum); } else { return new Range(); } } /// /// Returns the left value of the rectangle. /// /// The rectangle. /// The default value. /// The left value of the rectangle. public static double LeftOrDefault(this Rect rectangle, double value) { return rectangle.IsEmpty ? value : rectangle.Left; } /// /// Returns the right value of the rectangle. /// /// The rectangle. /// The default value. /// The right value of the rectangle. public static double RightOrDefault(this Rect rectangle, double value) { return rectangle.IsEmpty ? value : rectangle.Right; } /// /// Returns the width value of the rectangle. /// /// The rectangle. /// The default value. /// The width value of the rectangle. public static double WidthOrDefault(this Rect rectangle, double value) { return rectangle.IsEmpty ? value : rectangle.Width; } /// /// Returns the height value of the rectangle. /// /// The rectangle. /// The default value. /// The height value of the rectangle. public static double HeightOrDefault(this Rect rectangle, double value) { return rectangle.IsEmpty ? value : rectangle.Height; } /// /// Returns the bottom value of the rectangle. /// /// The rectangle. /// The default value. /// The bottom value of the rectangle. public static double BottomOrDefault(this Rect rectangle, double value) { return rectangle.IsEmpty ? value : rectangle.Bottom; } /// /// Returns the top value of the rectangle. /// /// The rectangle. /// The default value. /// The top value of the rectangle. public static double TopOrDefault(this Rect rectangle, double value) { return rectangle.IsEmpty ? value : rectangle.Top; } /// /// Converts any range to a range of IComparable. /// /// The range to be converted. /// The new range type. public static Range ToComparableRange(this Range range) { if (range.HasData) { return new Range(range.Minimum, range.Maximum); } else { return new Range(); } } /// /// Returns the time span of a date range. /// /// The range of values. /// The length of the range. public static TimeSpan? GetLength(this Range range) { return range.HasData ? range.Maximum - range.Minimum : new TimeSpan?(); } /// /// Returns the time span of a date range. /// /// The range of values. /// The length of the range. public static double? GetLength(this Range range) { return range.HasData ? range.Maximum - range.Minimum : new double?(); } /// /// Returns a value indicating whether a rectangle is empty or has /// no width or height. /// /// The rectangle. /// A value indicating whether a rectangle is empty or has /// no width or height. public static bool IsEmptyOrHasNoSize(this Rect rect) { return rect.IsEmpty || (rect.Width == 0 && rect.Height == 0); } /// /// Sets the style property of an element. /// /// The element. /// The style. public static void SetStyle(this FrameworkElement element, Style style) { element.Style = style; } } } ================================================ FILE: WpfToolkit/DataVisualization/WeakEventListener.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls { /// /// Implements a weak event listener that allows the owner to be garbage /// collected if its only remaining link is an event handler. /// /// Type of instance listening for the event. /// Type of source for the event. /// Type of event arguments for the event. [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Used as link target in several projects.")] internal class WeakEventListener where TInstance : class { /// /// WeakReference to the instance listening for the event. /// private WeakReference _weakInstance; /// /// Gets or sets the method to call when the event fires. /// public Action OnEventAction { get; set; } /// /// Gets or sets the method to call when detaching from the event. /// public Action> OnDetachAction { get; set; } /// /// Initializes a new instances of the WeakEventListener class. /// /// Instance subscribing to the event. public WeakEventListener(TInstance instance) { if (null == instance) { throw new ArgumentNullException("instance"); } _weakInstance = new WeakReference(instance); } /// /// Handler for the subscribed event calls OnEventAction to handle it. /// /// Event source. /// Event arguments. public void OnEvent(TSource source, TEventArgs eventArgs) { TInstance target = (TInstance)_weakInstance.Target; if (null != target) { // Call registered action if (null != OnEventAction) { OnEventAction(target, source, eventArgs); } } else { // Detach from event Detach(); } } /// /// Detaches from the subscribed event. /// public void Detach() { if (null != OnDetachAction) { OnDetachAction(this); OnDetachAction = null; } } } } ================================================ FILE: WpfToolkit/DataVisualization/WeakReferenceBag.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics; namespace System.Windows.Controls.DataVisualization { /// /// A bag of weak references to items. /// /// The type of the item. internal class WeakReferenceBag : IEnumerable where T : class { /// /// Gets or sets the list of event listeners. /// private IList Items { get; set; } /// /// Initializes a new instance of the WeakEvent class. /// public WeakReferenceBag() { this.Items = new List(); } /// /// Adds an item to the bag. /// /// The item to add to the bag. public void Add(T item) { Debug.Assert(item != null, "listener must not be null."); this.Items.Add(new WeakReference(item)); } /// /// Removes an item from the bag. /// /// The item to remove. public void Remove(T item) { Debug.Assert(item != null, "listener must not be null."); int count = 0; while (count < this.Items.Count) { object target = this.Items[count].Target; if (!this.Items[count].IsAlive || object.ReferenceEquals(target, item)) { this.Items.RemoveAt(count); } else { count++; } } } /// /// Returns a sequence of the elements in the bag. /// /// A sequence of the elements in the bag. public IEnumerator GetEnumerator() { int count = 0; while (count < this.Items.Count) { object target = this.Items[count].Target; if (!this.Items[count].IsAlive) { this.Items.RemoveAt(count); } else { yield return (T)target; count++; } } } /// /// Returns a sequence of the elements in the bag. /// /// A sequence of the elements in the bag. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return ((IEnumerable) this).GetEnumerator(); } } } ================================================ FILE: WpfToolkit/DatePicker/Microsoft/Windows/Automation/Peers/DatePickerAutomationPeer.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using Microsoft.Windows.Controls; namespace Microsoft.Windows.Automation.Peers { /// /// AutomationPeer for DatePicker Control /// public sealed class DatePickerAutomationPeer : FrameworkElementAutomationPeer, IExpandCollapseProvider, IValueProvider { /// /// Initializes a new instance of the AutomationPeer for DatePicker control. /// /// DatePicker public DatePickerAutomationPeer(DatePicker owner) : base(owner) { } #region Private Properties private DatePicker OwningDatePicker { get { return this.Owner as DatePicker; } } #endregion Private Properties #region Public Methods /// /// Gets the control pattern that is associated with the specified System.Windows.Automation.Peers.PatternInterface. /// /// A value from the System.Windows.Automation.Peers.PatternInterface enumeration. /// The object that supports the specified pattern, or null if unsupported. public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.ExpandCollapse || patternInterface == PatternInterface.Value) { return this; } return base.GetPattern(patternInterface); } #endregion Public Methods #region Protected Methods /// /// Gets the control type for the element that is associated with the UI Automation peer. /// /// The control type. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Custom; } /// /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, /// differentiates the control represented by this AutomationPeer. /// /// The string that contains the name. protected override string GetClassNameCore() { return Owner.GetType().Name; } /// /// Overrides the GetLocalizedControlTypeCore method for DatePicker /// /// protected override string GetLocalizedControlTypeCore() { return SR.Get(SRID.DatePickerAutomationPeer_LocalizedControlType); } #endregion Protected Methods #region IExpandCollapseProvider ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState { get { if (this.OwningDatePicker.IsDropDownOpen) { return ExpandCollapseState.Expanded; } else { return ExpandCollapseState.Collapsed; } } } void IExpandCollapseProvider.Collapse() { this.OwningDatePicker.IsDropDownOpen = false; } void IExpandCollapseProvider.Expand() { this.OwningDatePicker.IsDropDownOpen = true; } #endregion IExpandCollapseProvider #region IValueProvider bool IValueProvider.IsReadOnly { get { return false; } } string IValueProvider.Value { get { return this.OwningDatePicker.ToString(); } } void IValueProvider.SetValue(string value) { this.OwningDatePicker.Text = value; } #endregion IValueProvider #region Internal Methods // Never inline, as we don't want to unnecessarily link the automation DLL [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] internal void RaiseValuePropertyChangedEvent(string oldValue, string newValue) { if (oldValue != newValue) { RaisePropertyChangedEvent(ValuePatternIdentifiers.ValueProperty, oldValue, newValue); } } #endregion } } ================================================ FILE: WpfToolkit/DatePicker/Microsoft/Windows/Controls/DatePicker.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using Microsoft.Windows.Automation.Peers; using Microsoft.Windows.Controls.Primitives; namespace Microsoft.Windows.Controls { /// /// Represents a control that allows the user to select a date. /// [TemplatePart(Name = DatePicker.ElementRoot, Type = typeof(Grid))] [TemplatePart(Name = DatePicker.ElementTextBox, Type = typeof(DatePickerTextBox))] [TemplatePart(Name = DatePicker.ElementButton, Type = typeof(Button))] [TemplatePart(Name = DatePicker.ElementPopup, Type = typeof(Popup))] [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] public class DatePicker : Control { #region Constants private const string ElementRoot = "PART_Root"; private const string ElementTextBox = "PART_TextBox"; private const string ElementButton = "PART_Button"; private const string ElementPopup = "PART_Popup"; #endregion Constants #region Data private Calendar _calendar; private string _defaultText; private ButtonBase _dropDownButton; private Popup _popUp; private bool _disablePopupReopen; private bool _shouldCoerceText; private string _coercedTextValue; private DatePickerTextBox _textBox; private IDictionary _isHandlerSuspended; private DateTime? _originalSelectedDate; #endregion Data #region Public Events public static readonly RoutedEvent SelectedDateChangedEvent = EventManager.RegisterRoutedEvent("SelectedDateChanged", RoutingStrategy.Direct, typeof(EventHandler), typeof(DatePicker)); /// /// Occurs when the drop-down Calendar is closed. /// public event RoutedEventHandler CalendarClosed; /// /// Occurs when the drop-down Calendar is opened. /// public event RoutedEventHandler CalendarOpened; /// /// Occurs when text entered into the DatePicker cannot be parsed or the Date is not valid to be selected. /// public event EventHandler DateValidationError; /// /// Occurs when a date is selected. /// public event EventHandler SelectedDateChanged { add { AddHandler(SelectedDateChangedEvent, value); } remove { RemoveHandler(SelectedDateChangedEvent, value); } } #endregion Public Events /// /// Static constructor /// static DatePicker() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DatePicker), new FrameworkPropertyMetadata(typeof(DatePicker))); EventManager.RegisterClassHandler(typeof(DatePicker), UIElement.GotFocusEvent, new RoutedEventHandler(OnGotFocus)); KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(DatePicker), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(DatePicker), new FrameworkPropertyMetadata(false)); IsEnabledProperty.OverrideMetadata(typeof(DatePicker), new UIPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged))); } /// /// Initializes a new instance of the DatePicker class. /// public DatePicker() { InitializeCalendar(); this._defaultText = string.Empty; // Binding to FirstDayOfWeek and DisplayDate wont work this.FirstDayOfWeek = DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek; this.DisplayDate = DateTime.Today; } #region Public properties #region BlackoutDates /// /// Gets the days that are not selectable. /// public CalendarBlackoutDatesCollection BlackoutDates { get { return _calendar.BlackoutDates; } } #endregion BlackoutDates #region CalendarStyle /// /// Gets or sets the style that is used when rendering the calendar. /// public Style CalendarStyle { get { return (Style)GetValue(CalendarStyleProperty); } set { SetValue(CalendarStyleProperty, value); } } /// /// Identifies the CalendarStyle dependency property. /// public static readonly DependencyProperty CalendarStyleProperty = DependencyProperty.Register( "CalendarStyle", typeof(Style), typeof(DatePicker)); #endregion CalendarStyle #region DisplayDate /// /// Gets or sets the date to display. /// /// public DateTime DisplayDate { get { return (DateTime)GetValue(DisplayDateProperty); } set { SetValue(DisplayDateProperty, value); } } /// /// Identifies the DisplayDate dependency property. /// public static readonly DependencyProperty DisplayDateProperty = DependencyProperty.Register( "DisplayDate", typeof(DateTime), typeof(DatePicker), new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDisplayDate)); private static object CoerceDisplayDate(DependencyObject d, object value) { DatePicker dp = d as DatePicker; // We set _calendar.DisplayDate in order to get _calendar to compute the coerced value dp._calendar.DisplayDate = (DateTime)value; return dp._calendar.DisplayDate; } #endregion DisplayDate #region DisplayDateEnd /// /// Gets or sets the last date to be displayed. /// /// public DateTime? DisplayDateEnd { get { return (DateTime?)GetValue(DisplayDateEndProperty); } set { SetValue(DisplayDateEndProperty, value); } } /// /// Identifies the DisplayDateEnd dependency property. /// public static readonly DependencyProperty DisplayDateEndProperty = DependencyProperty.Register( "DisplayDateEnd", typeof(DateTime?), typeof(DatePicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateEndChanged, CoerceDisplayDateEnd)); /// /// DisplayDateEndProperty property changed handler. /// /// DatePicker that changed its DisplayDateEnd. /// DependencyPropertyChangedEventArgs. private static void OnDisplayDateEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); dp.CoerceValue(DisplayDateProperty); } private static object CoerceDisplayDateEnd(DependencyObject d, object value) { DatePicker dp = d as DatePicker; // We set _calendar.DisplayDateEnd in order to get _calendar to compute the coerced value dp._calendar.DisplayDateEnd = (DateTime?)value; return dp._calendar.DisplayDateEnd; } #endregion DisplayDateEnd #region DisplayDateStart /// /// Gets or sets the first date to be displayed. /// /// public DateTime? DisplayDateStart { get { return (DateTime?)GetValue(DisplayDateStartProperty); } set { SetValue(DisplayDateStartProperty, value); } } /// /// Identifies the DisplayDateStart dependency property. /// public static readonly DependencyProperty DisplayDateStartProperty = DependencyProperty.Register( "DisplayDateStart", typeof(DateTime?), typeof(DatePicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateStartChanged, CoerceDisplayDateStart)); /// /// DisplayDateStartProperty property changed handler. /// /// DatePicker that changed its DisplayDateStart. /// DependencyPropertyChangedEventArgs. private static void OnDisplayDateStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); dp.CoerceValue(DisplayDateEndProperty); dp.CoerceValue(DisplayDateProperty); } private static object CoerceDisplayDateStart(DependencyObject d, object value) { DatePicker dp = d as DatePicker; // We set _calendar.DisplayDateStart in order to get _calendar to compute the coerced value dp._calendar.DisplayDateStart = (DateTime?)value; return dp._calendar.DisplayDateStart; } #endregion DisplayDateStart #region FirstDayOfWeek /// /// Gets or sets the day that is considered the beginning of the week. /// public DayOfWeek FirstDayOfWeek { get { return (DayOfWeek)GetValue(FirstDayOfWeekProperty); } set { SetValue(FirstDayOfWeekProperty, value); } } /// /// Identifies the FirstDayOfWeek dependency property. /// public static readonly DependencyProperty FirstDayOfWeekProperty = DependencyProperty.Register( "FirstDayOfWeek", typeof(DayOfWeek), typeof(DatePicker), null, Calendar.IsValidFirstDayOfWeek); #endregion FirstDayOfWeek #region IsDropDownOpen /// /// Gets or sets a value that indicates whether the drop-down Calendar is open or closed. /// public bool IsDropDownOpen { get { return (bool)GetValue(IsDropDownOpenProperty); } set { SetValue(IsDropDownOpenProperty, value); } } /// /// Identifies the IsDropDownOpen dependency property. /// public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register( "IsDropDownOpen", typeof(bool), typeof(DatePicker), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsDropDownOpenChanged, OnCoerceIsDropDownOpen)); private static object OnCoerceIsDropDownOpen(DependencyObject d, object baseValue) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); if (!dp.IsEnabled) { return false; } return baseValue; } /// /// IsDropDownOpenProperty property changed handler. /// /// DatePicker that changed its IsDropDownOpen. /// DependencyPropertyChangedEventArgs. private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); bool newValue = (bool)e.NewValue; if (dp._popUp != null && dp._popUp.IsOpen != newValue) { dp._popUp.IsOpen = newValue; if (newValue) { dp._originalSelectedDate = dp.SelectedDate; // When the popup is opened set focus to the DisplayDate button. // Do this asynchronously because the IsDropDownOpen could // have been set even before the template for the DatePicker is // applied. And this would mean that the visuals wouldn't be available yet. dp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate() { // setting the focus to the calendar will focus the correct date. dp._calendar.Focus(); }); } } } #endregion IsDropDownOpen #region IsEnabled /// /// Called when the IsEnabled property changes. /// private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); dp.CoerceValue(IsDropDownOpenProperty); OnVisualStatePropertyChanged(dp); } private static void OnVisualStatePropertyChanged(DatePicker dp) { if (Validation.GetHasError(dp)) { if (dp.IsKeyboardFocused) { VisualStateManager.GoToState(dp, VisualStates.StateInvalidFocused, true); } else { VisualStateManager.GoToState(dp, VisualStates.StateInvalidUnfocused, true); } } else { VisualStateManager.GoToState(dp, VisualStates.StateValid, true); } } #endregion IsEnabled #region IsTodayHighlighted /// /// Gets or sets a value that indicates whether the current date will be highlighted. /// public bool IsTodayHighlighted { get { return (bool)GetValue(IsTodayHighlightedProperty); } set { SetValue(IsTodayHighlightedProperty, value); } } /// /// Identifies the IsTodayHighlighted dependency property. /// public static readonly DependencyProperty IsTodayHighlightedProperty = DependencyProperty.Register( "IsTodayHighlighted", typeof(bool), typeof(DatePicker)); #endregion IsTodayHighlighted #region SelectedDate /// /// Gets or sets the currently selected date. /// /// public DateTime? SelectedDate { get { return (DateTime?)GetValue(SelectedDateProperty); } set { SetValue(SelectedDateProperty, value); } } /// /// Identifies the SelectedDate dependency property. /// public static readonly DependencyProperty SelectedDateProperty = DependencyProperty.Register( "SelectedDate", typeof(DateTime?), typeof(DatePicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault , OnSelectedDateChanged, CoerceSelectedDate)); /// /// SelectedDateProperty property changed handler. /// /// DatePicker that changed its SelectedDate. /// DependencyPropertyChangedEventArgs. private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); Collection addedItems = new Collection(); Collection removedItems = new Collection(); DateTime? addedDate; DateTime? removedDate; dp.CoerceValue(DisplayDateStartProperty); dp.CoerceValue(DisplayDateEndProperty); dp.CoerceValue(DisplayDateProperty); addedDate = (DateTime?)e.NewValue; removedDate = (DateTime?)e.OldValue; if (dp.SelectedDate.HasValue) { DateTime day = dp.SelectedDate.Value; dp.SetTextInternal(dp.DateTimeToString(day)); // When DatePickerDisplayDateFlag is TRUE, the SelectedDate change is coming from the Calendar UI itself, // so, we shouldn't change the DisplayDate since it will automatically be changed by the Calendar if ((day.Month != dp.DisplayDate.Month || day.Year != dp.DisplayDate.Year) && !dp._calendar.DatePickerDisplayDateFlag) { dp.DisplayDate = day; } dp._calendar.DatePickerDisplayDateFlag = false; } else { dp.SetWaterMarkText(); } if (addedDate.HasValue) { addedItems.Add(addedDate.Value); } if (removedDate.HasValue) { removedItems.Add(removedDate.Value); } dp.OnSelectedDateChanged(new CalendarSelectionChangedEventArgs(DatePicker.SelectedDateChangedEvent, removedItems, addedItems)); DatePickerAutomationPeer peer = UIElementAutomationPeer.FromElement(dp) as DatePickerAutomationPeer; // Raise the propetyChangeEvent for Value if Automation Peer exist if (peer != null) { string addedDateString = addedDate.HasValue ? dp.DateTimeToString(addedDate.Value) : ""; string removedDateString = removedDate.HasValue ? dp.DateTimeToString(removedDate.Value) : ""; peer.RaiseValuePropertyChangedEvent(removedDateString, addedDateString); } } private static object CoerceSelectedDate(DependencyObject d, object value) { DatePicker dp = d as DatePicker; // We set _calendar.SelectedDate in order to get _calendar to compute the coerced value dp._calendar.SelectedDate = (DateTime?)value; return dp._calendar.SelectedDate; } #endregion SelectedDate #region SelectedDateFormat /// /// Gets or sets the format that is used to display the selected date. /// public DatePickerFormat SelectedDateFormat { get { return (DatePickerFormat)GetValue(SelectedDateFormatProperty); } set { SetValue(SelectedDateFormatProperty, value); } } /// /// Identifies the SelectedDateFormat dependency property. /// public static readonly DependencyProperty SelectedDateFormatProperty = DependencyProperty.Register( "SelectedDateFormat", typeof(DatePickerFormat), typeof(DatePicker), new FrameworkPropertyMetadata(DatePickerFormat.Long, OnSelectedDateFormatChanged), IsValidSelectedDateFormat); /// /// SelectedDateFormatProperty property changed handler. /// /// DatePicker that changed its SelectedDateFormat. /// DependencyPropertyChangedEventArgs. private static void OnSelectedDateFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); if (dp._textBox != null) { // Update DatePickerTextBox.Text if (string.IsNullOrEmpty(dp._textBox.Text)) { dp.SetWaterMarkText(); } else { DateTime? date = dp.ParseText(dp._textBox.Text); if (date != null) { dp.SetTextInternal(dp.DateTimeToString((DateTime)date)); } } } } #endregion SelectedDateFormat #region Text /// /// Gets or sets the text that is displayed by the DatePicker. /// public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } /// /// Identifies the Text dependency property. /// public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof(string), typeof(DatePicker), new FrameworkPropertyMetadata(string.Empty, OnTextChanged, OnCoerceText)); /// /// TextProperty property changed handler. /// /// DatePicker that changed its Text. /// DependencyPropertyChangedEventArgs. private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DatePicker dp = d as DatePicker; Debug.Assert(dp != null); if (!dp.IsHandlerSuspended(DatePicker.TextProperty)) { string newValue = e.NewValue as string; if (newValue != null) { if (dp._textBox != null) { dp._textBox.Text = newValue; } else { dp._defaultText = newValue; } dp.SetSelectedDate(); } else { dp.SetValueNoCallback(DatePicker.SelectedDateProperty, null); } } } private static object OnCoerceText(DependencyObject dObject, object baseValue) { DatePicker dp = (DatePicker)dObject; if (dp._shouldCoerceText) { dp._shouldCoerceText = false; return dp._coercedTextValue; } return baseValue; } /// /// Sets the local Text property without breaking bindings /// /// private void SetTextInternal(string value) { if (BindingOperations.GetBindingExpressionBase(this, DatePicker.TextProperty) != null) { Text = value; } else { _shouldCoerceText = true; _coercedTextValue = value; CoerceValue(TextProperty); } } #endregion Text #endregion Public Properties #region Protected properties #endregion Protected Properties #region Internal Properties #endregion Internal Properties #region Private Properties #endregion Private Properties #region Public Methods /// /// Builds the visual tree for the DatePicker control when a new template is applied. /// public override void OnApplyTemplate() { if (_popUp != null) { _popUp.RemoveHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(PopUp_PreviewMouseLeftButtonDown)); _popUp.Opened -= PopUp_Opened; _popUp.Closed -= PopUp_Closed; _popUp.Child = null; } if (_dropDownButton != null) { _dropDownButton.Click -= DropDownButton_Click; _dropDownButton.RemoveHandler(MouseLeaveEvent, new MouseEventHandler(DropDownButton_MouseLeave)); } if (_textBox != null) { _textBox.RemoveHandler(TextBox.KeyDownEvent, new KeyEventHandler(TextBox_KeyDown)); _textBox.RemoveHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(TextBox_TextChanged)); _textBox.RemoveHandler(TextBox.LostFocusEvent, new RoutedEventHandler(TextBox_LostFocus)); } base.OnApplyTemplate(); _popUp = GetTemplateChild(ElementPopup) as Popup; if (_popUp != null) { _popUp.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(PopUp_PreviewMouseLeftButtonDown)); _popUp.Opened += PopUp_Opened; _popUp.Closed += PopUp_Closed; _popUp.Child = this._calendar; if (this.IsDropDownOpen) { this._popUp.IsOpen = true; } } _dropDownButton = GetTemplateChild(ElementButton) as Button; if (_dropDownButton != null) { _dropDownButton.Click += DropDownButton_Click; _dropDownButton.AddHandler(MouseLeaveEvent, new MouseEventHandler(DropDownButton_MouseLeave), true); // If the user does not provide a Content value in template, we provide a helper text that can be used in Accessibility // this text is not shown on the UI, just used for Accessibility purposes if (_dropDownButton.Content == null) { _dropDownButton.Content = SR.Get(SRID.DatePicker_DropDownButtonName); } } _textBox = GetTemplateChild(ElementTextBox) as DatePickerTextBox; UpdateDisabledVisual(); if (this.SelectedDate == null) { SetWaterMarkText(); } if (_textBox != null) { _textBox.AddHandler(TextBox.KeyDownEvent, new KeyEventHandler(TextBox_KeyDown), true); _textBox.AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(TextBox_TextChanged), true); _textBox.AddHandler(TextBox.LostFocusEvent, new RoutedEventHandler(TextBox_LostFocus), true); if (this.SelectedDate == null) { if (!string.IsNullOrEmpty(this._defaultText)) { _textBox.Text = this._defaultText; SetSelectedDate(); } } else { _textBox.Text = this.DateTimeToString((DateTime)this.SelectedDate); } } } /// /// Provides a text representation of the selected date. /// /// A text representation of the selected date, or an empty string if SelectedDate is a null reference. public override string ToString() { if (this.SelectedDate != null) { return this.SelectedDate.Value.ToString(DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this))); } else { return string.Empty; } } #endregion Public Methods #region Protected Methods /// /// Creates the automation peer for this DatePicker Control. /// /// protected override AutomationPeer OnCreateAutomationPeer() { return new DatePickerAutomationPeer(this); } protected virtual void OnCalendarClosed(RoutedEventArgs e) { RoutedEventHandler handler = this.CalendarClosed; if (null != handler) { handler(this, e); } } protected virtual void OnCalendarOpened(RoutedEventArgs e) { RoutedEventHandler handler = this.CalendarOpened; if (null != handler) { handler(this, e); } } protected virtual void OnSelectedDateChanged(SelectionChangedEventArgs e) { RaiseEvent(e); } /// /// Raises the DateValidationError event. /// /// A DatePickerDateValidationErrorEventArgs that contains the event data. protected virtual void OnDateValidationError(DatePickerDateValidationErrorEventArgs e) { EventHandler handler = this.DateValidationError; if (handler != null) { handler(this, e); } } #endregion Protected Methods #region Private Methods /// /// Called when this element gets focus. /// private static void OnGotFocus(object sender, RoutedEventArgs e) { // When Datepicker gets focus move it to the TextBox DatePicker picker = (DatePicker)sender; if ((!e.Handled) && (picker._textBox != null)) { if (e.OriginalSource == picker) { picker._textBox.Focus(); e.Handled = true; } else if (e.OriginalSource == picker._textBox) { picker._textBox.SelectAll(); e.Handled = true; } } } private void SetValueNoCallback(DependencyProperty property, object value) { SetIsHandlerSuspended(property, true); try { SetValue(property, value); } finally { SetIsHandlerSuspended(property, false); } } private bool IsHandlerSuspended(DependencyProperty property) { return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property); } private void SetIsHandlerSuspended(DependencyProperty property, bool value) { if (value) { if (_isHandlerSuspended == null) { _isHandlerSuspended = new Dictionary(2); } _isHandlerSuspended[property] = true; } else { if (_isHandlerSuspended != null) { _isHandlerSuspended.Remove(property); } } } private void PopUp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Popup popup = sender as Popup; if (popup != null && !popup.StaysOpen) { if (this._dropDownButton != null) { if (this._dropDownButton.InputHitTest(e.GetPosition(this._dropDownButton)) != null) { // This popup is being closed by a mouse press on the drop down button // The following mouse release will cause the closed popup to immediately reopen. // Raise a flag to block reopeneing the popup this._disablePopupReopen = true; } } } } private void PopUp_Opened(object sender, EventArgs e) { if (!this.IsDropDownOpen) { this.IsDropDownOpen = true; } if (this._calendar != null) { this._calendar.DisplayMode = CalendarMode.Month; this._calendar.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } this.OnCalendarOpened(new RoutedEventArgs()); } private void PopUp_Closed(object sender, EventArgs e) { if (this.IsDropDownOpen) { this.IsDropDownOpen = false; } if (_calendar.IsKeyboardFocusWithin) { this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } OnCalendarClosed(new RoutedEventArgs()); } private void Calendar_DayButtonMouseUp(object sender, MouseButtonEventArgs e) { this.IsDropDownOpen = false; } private void Calendar_DisplayDateChanged(object sender, CalendarDateChangedEventArgs e) { if (e.AddedDate != this.DisplayDate) { SetValue(DisplayDateProperty, (DateTime)e.AddedDate); } } private void CalendarDayOrMonthButton_PreviewKeyDown(object sender, RoutedEventArgs e) { Calendar c = sender as Calendar; KeyEventArgs args = (KeyEventArgs)e; Debug.Assert(c != null); Debug.Assert(args != null); if (args.Key == Key.Escape || ((args.Key == Key.Enter || args.Key == Key.Space) && c.DisplayMode == CalendarMode.Month)) { this.IsDropDownOpen = false; if (args.Key == Key.Escape) { SelectedDate = _originalSelectedDate; } } } private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e) { Debug.Assert(e.AddedItems.Count < 2); if (e.AddedItems.Count > 0 && this.SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0], this.SelectedDate.Value) != 0) { this.SelectedDate = (DateTime?)e.AddedItems[0]; } else { if (e.AddedItems.Count == 0) { this.SelectedDate = null; return; } if (!this.SelectedDate.HasValue) { if (e.AddedItems.Count > 0) { this.SelectedDate = (DateTime?)e.AddedItems[0]; } } } } private string DateTimeToString(DateTime d) { DateTimeFormatInfo dtfi = DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)); switch (this.SelectedDateFormat) { case DatePickerFormat.Short: { return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.ShortDatePattern, dtfi)); } case DatePickerFormat.Long: { return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.LongDatePattern, dtfi)); } } return null; } private static DateTime DiscardDayTime(DateTime d) { int year = d.Year; int month = d.Month; DateTime newD = new DateTime(year, month, 1, 0, 0, 0); return newD; } private static DateTime? DiscardTime(DateTime? d) { if (d == null) { return null; } else { DateTime discarded = (DateTime)d; int year = discarded.Year; int month = discarded.Month; int day = discarded.Day; DateTime newD = new DateTime(year, month, day, 0, 0, 0); return newD; } } private void DropDownButton_Click(object sender, RoutedEventArgs e) { TogglePopUp(); } private void DropDownButton_MouseLeave(object sender, MouseEventArgs e) { this._disablePopupReopen = false; } private void TogglePopUp() { if (this.IsDropDownOpen) { this.IsDropDownOpen = false; } else { if (this._disablePopupReopen) { this._disablePopupReopen = false; } else { SetSelectedDate(); this.IsDropDownOpen = true; } } } private void InitializeCalendar() { _calendar = new Calendar(); _calendar.DayButtonMouseUp += new MouseButtonEventHandler(Calendar_DayButtonMouseUp); _calendar.DisplayDateChanged += new EventHandler(Calendar_DisplayDateChanged); _calendar.SelectedDatesChanged += new EventHandler(Calendar_SelectedDatesChanged); _calendar.DayOrMonthPreviewKeyDown += new RoutedEventHandler(CalendarDayOrMonthButton_PreviewKeyDown); _calendar.HorizontalAlignment = HorizontalAlignment.Left; _calendar.VerticalAlignment = VerticalAlignment.Top; _calendar.SelectionMode = CalendarSelectionMode.SingleDate; _calendar.SetBinding(Calendar.ForegroundProperty, GetDatePickerBinding(DatePicker.ForegroundProperty)); _calendar.SetBinding(Calendar.StyleProperty, GetDatePickerBinding(DatePicker.CalendarStyleProperty)); _calendar.SetBinding(Calendar.IsTodayHighlightedProperty, GetDatePickerBinding(DatePicker.IsTodayHighlightedProperty)); _calendar.SetBinding(Calendar.FirstDayOfWeekProperty, GetDatePickerBinding(DatePicker.FirstDayOfWeekProperty)); } private BindingBase GetDatePickerBinding(DependencyProperty property) { Binding binding = new Binding(property.Name); binding.Source = this; return binding; } private static bool IsValidSelectedDateFormat(object value) { DatePickerFormat format = (DatePickerFormat)value; return format == DatePickerFormat.Long || format == DatePickerFormat.Short; } // iT SHOULD RETURN NULL IF THE STRING IS NOT VALID, RETURN THE DATETIME VALUE IF IT IS VALID /// /// Input text is parsed in the correct format and changed into a DateTime object. /// If the text can not be parsed TextParseError Event is thrown. /// private DateTime? ParseText(string text) { DateTime newSelectedDate; // TryParse is not used in order to be able to pass the exception to the TextParseError event try { newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this))); if (Calendar.IsValidDateSelection(this._calendar, newSelectedDate)) { return newSelectedDate; } else { DatePickerDateValidationErrorEventArgs dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException("text", SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue)), text); OnDateValidationError(dateValidationError); if (dateValidationError.ThrowException) { throw dateValidationError.Exception; } } } catch (FormatException ex) { DatePickerDateValidationErrorEventArgs textParseError = new DatePickerDateValidationErrorEventArgs(ex, text); OnDateValidationError(textParseError); if (textParseError.ThrowException && textParseError.Exception != null) { throw textParseError.Exception; } } return null; } private bool ProcessDatePickerKey(KeyEventArgs e) { switch (e.Key) { case Key.System: { switch (e.SystemKey) { case Key.Down: { if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) { TogglePopUp(); return true; } break; } } break; } case Key.Enter: { SetSelectedDate(); return true; } } return false; } private void SetSelectedDate() { if (this._textBox != null) { if (!string.IsNullOrEmpty(this._textBox.Text)) { string s = this._textBox.Text; if (this.SelectedDate != null) { // If the string value of the SelectedDate and the TextBox string value are equal, // we do not parse the string again // if we do an extra parse, we lose data in M/d/yy format // ex: SelectedDate = DateTime(1008,12,19) but when "12/19/08" is parsed it is interpreted as DateTime(2008,12,19) string selectedDate = DateTimeToString(this.SelectedDate.Value); if (selectedDate == s) { return; } } DateTime? d = SetTextBoxValue(s); if (!this.SelectedDate.Equals(d)) { this.SelectedDate = d; this.DisplayDate = d.Value; } } else { if (this.SelectedDate != null) { this.SelectedDate = null; } } } else { DateTime? d = SetTextBoxValue(_defaultText); if (!this.SelectedDate.Equals(d)) { this.SelectedDate = d; } } } private DateTime? SetTextBoxValue(string s) { if (string.IsNullOrEmpty(s)) { SetValue(TextProperty, s); return this.SelectedDate; } else { DateTime? d = ParseText(s); if (d != null) { SetValue(TextProperty, this.DateTimeToString((DateTime)d)); return d; } else { // If parse error: // TextBox should have the latest valid selecteddate value: if (this.SelectedDate != null) { string newtext = this.DateTimeToString((DateTime)this.SelectedDate); SetValue(TextProperty, newtext); return this.SelectedDate; } else { SetWaterMarkText(); return null; } } } } private void SetWaterMarkText() { if (this._textBox != null) { DateTimeFormatInfo dtfi = DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)); this.SetTextInternal(string.Empty); this._defaultText = string.Empty; switch (this.SelectedDateFormat) { case DatePickerFormat.Long: { this._textBox.Watermark = string.Format(CultureInfo.CurrentCulture, SR.Get(SRID.DatePicker_WatermarkText), dtfi.LongDatePattern.ToString()); break; } case DatePickerFormat.Short: { this._textBox.Watermark = string.Format(CultureInfo.CurrentCulture, SR.Get(SRID.DatePicker_WatermarkText), dtfi.ShortDatePattern.ToString()); break; } } } } private void TextBox_LostFocus(object sender, RoutedEventArgs e) { SetSelectedDate(); } private void TextBox_KeyDown(object sender, KeyEventArgs e) { e.Handled = ProcessDatePickerKey(e) || e.Handled; } private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { this.SetValueNoCallback(DatePicker.TextProperty, this._textBox.Text); } private void UpdateDisabledVisual() { if (!IsEnabled) { VisualStates.GoToState(this, true, VisualStates.StateDisabled, VisualStates.StateNormal); } else { VisualStates.GoToState(this, true, VisualStates.StateNormal); } } #endregion Private Methods } } ================================================ FILE: WpfToolkit/DatePicker/Microsoft/Windows/Controls/DatePickerDateValidationErrorEventArgs.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; namespace Microsoft.Windows.Controls { /// /// Provides data for the DateValidationError event. /// public class DatePickerDateValidationErrorEventArgs : EventArgs { private bool _throwException; /// /// Initializes a new instance of the DatePickerDateValidationErrorEventArgs class. /// /// The exception that initially triggered the DateValidationError event. /// The text being parsed. public DatePickerDateValidationErrorEventArgs(Exception exception, string text) { this.Text = text; this.Exception = exception; } /// /// Gets the exception that initially triggered the DateValidationError event. /// public Exception Exception { get; private set; } /// /// Gets the text being parsed when the DateValidationError event was raised. /// public string Text { get; private set; } /// /// Gets or sets a value that indicates whether Exception should be thrown. /// public bool ThrowException { get { return this._throwException; } set { this._throwException = value; } } } } ================================================ FILE: WpfToolkit/DatePicker/Microsoft/Windows/Controls/DatePickerFormat.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- namespace Microsoft.Windows.Controls { /// /// Specifies date formats for a DatePicker. /// public enum DatePickerFormat { /// /// Specifies that the date should be displayed using unabbreviated days of the week and month names. /// Long = 0, /// /// Specifies that the date should be displayed using abbreviated days of the week and month names. /// Short = 1 } } ================================================ FILE: WpfToolkit/DatePicker/Microsoft/Windows/Controls/DatePickerTextBox.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System.Diagnostics; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace Microsoft.Windows.Controls.Primitives { /// /// DatePickerTextBox is a specialized form of TextBox which displays custom visuals when its contents are empty /// [TemplatePart(Name = DatePickerTextBox.ElementContentName, Type = typeof(ContentControl))] [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnwatermarked, GroupName = VisualStates.GroupWatermark)] [TemplateVisualState(Name = VisualStates.StateWatermarked, GroupName = VisualStates.GroupWatermark)] public sealed partial class DatePickerTextBox : TextBox { #region Constants private const string ElementContentName = "Watermark"; #endregion #region Data private ContentControl elementContent; private bool isHovered; #endregion #region Constructor /// /// Static constructor /// static DatePickerTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DatePickerTextBox), new FrameworkPropertyMetadata(typeof(DatePickerTextBox))); } /// /// Initializes a new instance of the class. /// public DatePickerTextBox() { this.Watermark = SR.Get(SRID.DatePickerTextBox_DefaultWatermarkText); this.Loaded += OnLoaded; this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(OnDatePickerTextBoxIsEnabledChanged); } #endregion #region Public Properties #region Watermark /// /// Watermark dependency property /// internal static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register( "Watermark", typeof(object), typeof(DatePickerTextBox), new PropertyMetadata(OnWatermarkPropertyChanged)); /// /// Watermark content /// /// The watermark. internal object Watermark { get { return (object)GetValue(WatermarkProperty); } set { SetValue(WatermarkProperty, value); } } #endregion #endregion Public Properties #region Protected /// /// Called when template is applied to the control. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); elementContent = ExtractTemplatePart(ElementContentName); OnWatermarkChanged(); ChangeVisualState(false); } protected override void OnGotFocus(RoutedEventArgs e) { base.OnGotFocus(e); if (IsEnabled) { if (!string.IsNullOrEmpty(this.Text)) { Select(0, this.Text.Length); } ChangeVisualState(true); } } protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); ChangeVisualState(true); } protected override void OnMouseEnter(MouseEventArgs e) { base.OnMouseEnter(e); this.isHovered = true; if (!IsFocused) { ChangeVisualState(true); } } protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); isHovered = false; if (!IsFocused) { ChangeVisualState(true); } } protected override void OnTextChanged(TextChangedEventArgs e) { base.OnTextChanged(e); ChangeVisualState(true); } #endregion Protected #region Private private void OnLoaded(object sender, RoutedEventArgs e) { ApplyTemplate(); ChangeVisualState(false); } /// /// Change to the correct visual state for the textbox. /// /// /// true to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// private void ChangeVisualState(bool useTransitions) { // Update the CommonStates group if (!IsEnabled) { VisualStates.GoToState(this, useTransitions, VisualStates.StateDisabled, VisualStates.StateNormal); } else if (isHovered) { VisualStates.GoToState(this, useTransitions, VisualStates.StateMouseOver, VisualStates.StateNormal); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateNormal); } // Update the FocusStates group if (IsFocused && IsEnabled) { VisualStates.GoToState(this, useTransitions, VisualStates.StateFocused, VisualStates.StateUnfocused); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateUnfocused); } // Update the WatermarkStates group if (this.Watermark != null && string.IsNullOrEmpty(this.Text)) { VisualStates.GoToState(this, useTransitions, VisualStates.StateWatermarked, VisualStates.StateUnwatermarked); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateUnwatermarked); } } private T ExtractTemplatePart(string partName) where T : DependencyObject { DependencyObject obj = GetTemplateChild(partName); return ExtractTemplatePart(partName, obj); } private static T ExtractTemplatePart(string partName, DependencyObject obj) where T : DependencyObject { Debug.Assert( obj == null || typeof(T).IsInstanceOfType(obj), string.Format(CultureInfo.InvariantCulture, SR.Get(SRID.DatePickerTextBox_TemplatePartIsOfIncorrectType), partName, typeof(T).Name)); return obj as T; } /// /// Called when the IsEnabled property changes. /// /// Sender object /// Property changed args private void OnDatePickerTextBoxIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { Debug.Assert(e.NewValue is bool); bool isEnabled = (bool)e.NewValue; IsReadOnly = !isEnabled; ChangeVisualState(true); } private void OnWatermarkChanged() { if (elementContent != null) { Control watermarkControl = this.Watermark as Control; if (watermarkControl != null) { watermarkControl.IsTabStop = false; watermarkControl.IsHitTestVisible = false; } } } /// /// Called when watermark property is changed. /// /// The sender. /// The instance containing the event data. private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { DatePickerTextBox datePickerTextBox = sender as DatePickerTextBox; Debug.Assert(datePickerTextBox != null, "The source is not an instance of a DatePickerTextBox!"); datePickerTextBox.OnWatermarkChanged(); datePickerTextBox.ChangeVisualState(true); } #endregion Private } } ================================================ FILE: WpfToolkit/DatePicker/Themes/Aero.NormalColor.xaml ================================================  Aero.NormalColor ================================================ FILE: WpfToolkit/DatePicker/Themes/Classic.xaml ================================================  Classic ================================================ FILE: WpfToolkit/DatePicker/Themes/Generic.xaml ================================================  ================================================ FILE: WpfToolkit/DatePicker/Themes/Luna.HomeStead.xaml ================================================  Luna.HomeStead ================================================ FILE: WpfToolkit/DatePicker/Themes/Luna.Metallic.xaml ================================================  Luna.Metallic ================================================ FILE: WpfToolkit/DatePicker/Themes/Luna.NormalColor.xaml ================================================  Luna.NormalColor ================================================ FILE: WpfToolkit/DatePicker/Themes/Royale.NormalColor.xaml ================================================  Royale.NormalColor ================================================ FILE: WpfToolkit/GlobalSuppressions.cs ================================================ // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "member", Target = "Microsoft.Windows.Controls.DataGrid.#GridLinesVisibility")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "member", Target = "Microsoft.Windows.Controls.DataGrid.#GridLinesVisibilityProperty")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "member", Target = "Microsoft.Windows.Controls.DataGrid.#HorizontalGridLinesBrush")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "member", Target = "Microsoft.Windows.Controls.DataGrid.#HorizontalGridLinesBrushProperty")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "member", Target = "Microsoft.Windows.Controls.DataGrid.#VerticalGridLinesBrush")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "member", Target = "Microsoft.Windows.Controls.DataGrid.#VerticalGridLinesBrushProperty")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Scope = "member", Target = "Microsoft.Windows.Controls.DataGridColumn.#ActualWidthPropertyKey")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Scope = "type", Target = "Microsoft.Windows.Controls.DataGridGridLinesVisibility")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames", Scope = "type", Target = "Microsoft.Windows.Controls.DataGridHeadersVisibility")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Scope = "member", Target = "Microsoft.Windows.Controls.DataGridRow.#GetRowContainingElement(System.Windows.FrameworkElement)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#Calendar_OnDisplayModePropertyChanged_InvalidValue")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#Calendar_OnFirstDayOfWeekChanged_InvalidValue")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#Calendar_OnSelectionModeChanged_InvalidValue")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_BeginEditCommandText")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_CancelEditCommandText")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_CommitEditCommandText")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_DeleteCommandText")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_NewColumnInvalidDisplayIndex")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_SelectAllCommandText")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_SelectAllKey")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DataGrid_SelectAllKeyDisplayString")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DatePicker_OnSelectedDateFormatChanged_InvalidValue")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.SRID.#DatePickerTextBox_TemplatePartIsOfIncorrectType")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Windows.Controls.VirtualizedCellInfoCollection.#IsValidCell(Microsoft.Windows.Controls.DataGridCellInfo)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "o", Scope = "member", Target = "Microsoft.Windows.Controls.VisualStateBehavior.#UpdateStateHandler(System.Object,System.EventArgs)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", Scope = "member", Target = "Microsoft.Windows.Controls.VisualStateBehavior.#UpdateStateHandler(System.Object,System.EventArgs)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Scope = "member", Target = "System.Windows.VisualStateManager.#GetCustomVisualStateManager(System.Windows.FrameworkElement)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Scope = "member", Target = "System.Windows.VisualStateManager.#RaiseCurrentStateChanged(System.Windows.VisualStateGroup,System.Windows.VisualState,System.Windows.VisualState,System.Windows.Controls.Control)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Scope = "member", Target = "System.Windows.VisualStateManager.#RaiseCurrentStateChanging(System.Windows.VisualStateGroup,System.Windows.VisualState,System.Windows.VisualState,System.Windows.Controls.Control)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Scope = "member", Target = "System.Windows.VisualStateManager.#SetCustomVisualStateManager(System.Windows.FrameworkElement,System.Windows.VisualStateManager)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "WPF")] ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Automation/Peers/AutoCompleteBoxAutomationPeer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Windows.Automation.Provider; using System.Windows.Controls; namespace System.Windows.Automation.Peers { /// /// Exposes AutoCompleteBox types to UI Automation. /// /// Stable public sealed class AutoCompleteBoxAutomationPeer : FrameworkElementAutomationPeer, IValueProvider, IExpandCollapseProvider, ISelectionProvider { /// /// The name reported as the core class name. /// private const string AutoCompleteBoxClassNameCore = "AutoCompleteBox"; /// /// Gets the AutoCompleteBox that owns this /// AutoCompleteBoxAutomationPeer. /// private AutoCompleteBox OwnerAutoCompleteBox { get { return (AutoCompleteBox)Owner; } } /// /// Gets a value indicating whether the UI automation provider allows /// more than one child element to be selected concurrently. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// /// True if multiple selection is allowed; otherwise, false. bool ISelectionProvider.CanSelectMultiple { get { return false; } } /// /// Gets a value indicating whether the UI automation provider /// requires at least one child element to be selected. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// /// True if selection is required; otherwise, false. bool ISelectionProvider.IsSelectionRequired { get { return false; } } /// /// Initializes a new instance of the AutoCompleteBoxAutomationPeer /// class. /// /// /// The AutoCompleteBox that is associated with this /// AutoCompleteBoxAutomationPeer. /// public AutoCompleteBoxAutomationPeer(AutoCompleteBox owner) : base(owner) { } /// /// Gets the control type for the AutoCompleteBox that is associated /// with this AutoCompleteBoxAutomationPeer. This method is called by /// GetAutomationControlType. /// /// ComboBox AutomationControlType. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.ComboBox; } /// /// Gets the name of the AutoCompleteBox that is associated with this /// AutoCompleteBoxAutomationPeer. This method is called by /// GetClassName. /// /// The name AutoCompleteBox. protected override string GetClassNameCore() { return AutoCompleteBoxClassNameCore; } /// /// Gets the control pattern for the AutoCompleteBox that is associated /// with this AutoCompleteBoxAutomationPeer. /// /// The desired PatternInterface. /// The desired AutomationPeer or null. public override object GetPattern(PatternInterface patternInterface) { object iface = null; AutoCompleteBox owner = OwnerAutoCompleteBox; if (patternInterface == PatternInterface.Value) { iface = this; } else if (patternInterface == PatternInterface.ExpandCollapse) { iface = this; } else if (owner.SelectionAdapter != null) { AutomationPeer peer = owner.SelectionAdapter.CreateAutomationPeer(); if (peer != null) { iface = peer.GetPattern(patternInterface); } } if (iface == null) { iface = base.GetPattern(patternInterface); } return iface; } #region ExpandCollapse /// /// Blocking method that returns after the element has been expanded. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void IExpandCollapseProvider.Expand() { if (!IsEnabled()) { throw new ElementNotEnabledException(); } OwnerAutoCompleteBox.IsDropDownOpen = true; } /// /// Blocking method that returns after the element has been collapsed. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void IExpandCollapseProvider.Collapse() { if (!IsEnabled()) { throw new ElementNotEnabledException(); } OwnerAutoCompleteBox.IsDropDownOpen = false; } /// /// Gets an element's current Collapsed or Expanded state. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState { get { return OwnerAutoCompleteBox.IsDropDownOpen ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; } } /// /// Raises the ExpandCollapse automation event. /// /// The old value. /// The new value. internal void RaiseExpandCollapseAutomationEvent(bool oldValue, bool newValue) { RaisePropertyChangedEvent( ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, oldValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed, newValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed); } #endregion ExpandCollapse #region ValueProvider /// /// Sets the value of a control. /// /// The value to set. The provider is responsible /// for converting the value to the appropriate data type. void IValueProvider.SetValue(string value) { OwnerAutoCompleteBox.Text = value; } /// /// Gets a value indicating whether the value of a control is /// read-only. /// /// True if the value is read-only; false if it can be modified. bool IValueProvider.IsReadOnly { get { return !OwnerAutoCompleteBox.IsEnabled; } } /// /// Gets the value of the control. /// /// The value of the control. string IValueProvider.Value { get { return OwnerAutoCompleteBox.Text ?? string.Empty; } } #endregion /// /// Gets the collection of child elements of the AutoCompleteBox that /// are associated with this AutoCompleteBoxAutomationPeer. This method /// is called by GetChildren. /// /// /// A collection of automation peer elements, or an empty collection /// if there are no child elements. /// [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "Required by automation")] protected override List GetChildrenCore() { List children = new List(); AutoCompleteBox owner = OwnerAutoCompleteBox; // TextBox part. TextBox textBox = owner.TextBox; if (textBox != null) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(textBox); if (peer != null) { children.Insert(0, peer); } } // Include SelectionAdapter's children. if (owner.SelectionAdapter != null) { AutomationPeer selectionAdapterPeer = owner.SelectionAdapter.CreateAutomationPeer(); if (selectionAdapterPeer != null) { List listChildren = selectionAdapterPeer.GetChildren(); if (listChildren != null) { foreach (AutomationPeer child in listChildren) { children.Add(child); } } } } return children; } /// /// Retrieves a UI automation provider for each child element that is /// selected. /// /// An array of UI automation providers. /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// IRawElementProviderSimple[] ISelectionProvider.GetSelection() { if (OwnerAutoCompleteBox.SelectionAdapter != null) { object selectedItem = OwnerAutoCompleteBox.SelectionAdapter.SelectedItem; if (selectedItem != null) { UIElement uie = selectedItem as UIElement; if (uie != null) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(uie); if (peer != null) { return new IRawElementProviderSimple[] { ProviderFromPeer(peer) }; } } } } return new IRawElementProviderSimple[] { }; } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/AutoCompleteBox.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Windows.Automation.Peers; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Threading; namespace System.Windows.Controls { /// /// Represents a control that provides a text box for user input and a /// drop-down that contains possible matches based on the input in the text /// box. /// /// Stable [TemplatePart(Name = AutoCompleteBox.ElementSelectionAdapter, Type = typeof(ISelectionAdapter))] [TemplatePart(Name = AutoCompleteBox.ElementSelector, Type = typeof(Selector))] [TemplatePart(Name = AutoCompleteBox.ElementTextBox, Type = typeof(TextBox))] [TemplatePart(Name = AutoCompleteBox.ElementPopup, Type = typeof(Popup))] [StyleTypedProperty(Property = AutoCompleteBox.ElementTextBoxStyle, StyleTargetType = typeof(TextBox))] [StyleTypedProperty(Property = AutoCompleteBox.ElementItemContainerStyle, StyleTargetType = typeof(ListBox))] [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StatePopupClosed, GroupName = VisualStates.GroupPopup)] [TemplateVisualState(Name = VisualStates.StatePopupOpened, GroupName = VisualStates.GroupPopup)] [TemplateVisualState(Name = VisualStates.StateValid, GroupName = VisualStates.GroupValidation)] [TemplateVisualState(Name = VisualStates.StateInvalidFocused, GroupName = VisualStates.GroupValidation)] [TemplateVisualState(Name = VisualStates.StateInvalidUnfocused, GroupName = VisualStates.GroupValidation)] [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Large implementation keeps the components contained.")] [ContentProperty("ItemsSource")] public partial class AutoCompleteBox : Control, IUpdateVisualState { #region Template part and style names /// /// Specifies the name of the selection adapter TemplatePart. /// private const string ElementSelectionAdapter = "SelectionAdapter"; /// /// Specifies the name of the Selector TemplatePart. /// private const string ElementSelector = "Selector"; /// /// Specifies the name of the Popup TemplatePart. /// private const string ElementPopup = "Popup"; /// /// The name for the text box part. /// private const string ElementTextBox = "Text"; /// /// The name for the text box style. /// private const string ElementTextBoxStyle = "TextBoxStyle"; /// /// The name for the adapter's item container style. /// private const string ElementItemContainerStyle = "ItemContainerStyle"; #endregion /// /// Gets or sets a local cached copy of the items data. /// private List _items; /// /// Gets or sets the observable collection that contains references to /// all of the items in the generated view of data that is provided to /// the selection-style control adapter. /// private ObservableCollection _view; /// /// Gets or sets a value to ignore a number of pending change handlers. /// The value is decremented after each use. This is used to reset the /// value of properties without performing any of the actions in their /// change handlers. /// /// The int is important as a value because the TextBox /// TextChanged event does not immediately fire, and this will allow for /// nested property changes to be ignored. private int _ignoreTextPropertyChange; /// /// Gets or sets a value indicating whether to ignore calling a pending /// change handlers. /// private bool _ignorePropertyChange; /// /// Gets or sets a value indicating whether to ignore the selection /// changed event. /// private bool _ignoreTextSelectionChange; /// /// Gets or sets a value indicating whether to skip the text update /// processing when the selected item is updated. /// private bool _skipSelectedItemTextUpdate; /// /// Gets or sets the last observed text box selection start location. /// private int _textSelectionStart; /// /// Gets or sets a value indicating whether the user initiated the /// current populate call. /// private bool _userCalledPopulate; /// /// A value indicating whether the popup has been opened at least once. /// private bool _popupHasOpened; /// /// Gets or sets the DispatcherTimer used for the MinimumPopulateDelay /// condition for auto completion. /// private DispatcherTimer _delayTimer; /// /// Gets or sets a value indicating whether a read-only dependency /// property change handler should allow the value to be set. This is /// used to ensure that read-only properties cannot be changed via /// SetValue, etc. /// private bool _allowWrite; /// /// Gets or sets the helper that provides all of the standard /// interaction functionality. Making it internal for subclass access. /// internal InteractionHelper Interaction { get; set; } /// /// A weak event listener for the collection changed event. /// private WeakEventListener _collectionChangedWeakEventListener; #region public int MinimumPrefixLength /// /// Gets or sets the minimum number of characters required to be entered /// in the text box before the /// displays /// possible matches. /// matches. /// /// /// The minimum number of characters to be entered in the text box /// before the /// displays possible matches. The default is 1. /// /// /// If you set MinimumPrefixLength to -1, the AutoCompleteBox will /// not provide possible matches. There is no maximum value, but /// setting MinimumPrefixLength to value that is too large will /// prevent the AutoCompleteBox from providing possible matches as well. /// public int MinimumPrefixLength { get { return (int)GetValue(MinimumPrefixLengthProperty); } set { SetValue(MinimumPrefixLengthProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty MinimumPrefixLengthProperty = DependencyProperty.Register( "MinimumPrefixLength", typeof(int), typeof(AutoCompleteBox), new PropertyMetadata(1, OnMinimumPrefixLengthPropertyChanged)); /// /// MinimumPrefixLengthProperty property changed handler. /// /// AutoCompleteBox that changed its MinimumPrefixLength. /// Event arguments. [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "MinimumPrefixLength is the name of the actual dependency property.")] private static void OnMinimumPrefixLengthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { int newValue = (int)e.NewValue; if (newValue < 0 && newValue != -1) { throw new ArgumentOutOfRangeException("MinimumPrefixLength"); } } #endregion public int MinimumPrefixLength #region public int MinimumPopulateDelay /// /// Gets or sets the minimum delay, in milliseconds, after text is typed /// in the text box before the /// control /// populates the list of possible matches in the drop-down. /// /// The minimum delay, in milliseconds, after text is typed in /// the text box, but before the /// populates /// the list of possible matches in the drop-down. The default is 0. /// The set value is less than 0. public int MinimumPopulateDelay { get { return (int)GetValue(MinimumPopulateDelayProperty); } set { SetValue(MinimumPopulateDelayProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty MinimumPopulateDelayProperty = DependencyProperty.Register( "MinimumPopulateDelay", typeof(int), typeof(AutoCompleteBox), new PropertyMetadata(OnMinimumPopulateDelayPropertyChanged)); /// /// MinimumPopulateDelayProperty property changed handler. Any current /// dispatcher timer will be stopped. The timer will not be restarted /// until the next TextUpdate call by the user. /// /// AutoCompleteTextBox that changed its /// MinimumPopulateDelay. /// Event arguments. [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The exception is most likely to be called through the CLR property setter.")] private static void OnMinimumPopulateDelayPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; if (source._ignorePropertyChange) { source._ignorePropertyChange = false; return; } int newValue = (int)e.NewValue; if (newValue < 0) { source._ignorePropertyChange = true; d.SetValue(e.Property, e.OldValue); throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.AutoComplete_OnMinimumPopulateDelayPropertyChanged_InvalidValue, newValue), "value"); } // Stop any existing timer if (source._delayTimer != null) { source._delayTimer.Stop(); if (newValue == 0) { source._delayTimer = null; } } // Create or clear a dispatcher timer instance if (newValue > 0 && source._delayTimer == null) { source._delayTimer = new DispatcherTimer(); source._delayTimer.Tick += source.PopulateDropDown; } // Set the new tick interval if (newValue > 0 && source._delayTimer != null) { source._delayTimer.Interval = TimeSpan.FromMilliseconds(newValue); } } #endregion public int MinimumPopulateDelay #region public bool IsTextCompletionEnabled /// /// Gets or sets a value indicating whether the first possible match /// found during the filtering process will be displayed automatically /// in the text box. /// /// /// True if the first possible match found will be displayed /// automatically in the text box; otherwise, false. The default is /// false. /// public bool IsTextCompletionEnabled { get { return (bool)GetValue(IsTextCompletionEnabledProperty); } set { SetValue(IsTextCompletionEnabledProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty IsTextCompletionEnabledProperty = DependencyProperty.Register( "IsTextCompletionEnabled", typeof(bool), typeof(AutoCompleteBox), new PropertyMetadata(false, null)); #endregion public bool IsTextCompletionEnabled #region public DataTemplate ItemTemplate /// /// Gets or sets the used /// to display each item in the drop-down portion of the control. /// /// The used to /// display each item in the drop-down. The default is null. /// /// You use the ItemTemplate property to specify the visualization /// of the data objects in the drop-down portion of the AutoCompleteBox /// control. If your AutoCompleteBox is bound to a collection and you /// do not provide specific display instructions by using a /// DataTemplate, the resulting UI of each item is a string /// representation of each object in the underlying collection. /// public DataTemplate ItemTemplate { get { return GetValue(ItemTemplateProperty) as DataTemplate; } set { SetValue(ItemTemplateProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register( "ItemTemplate", typeof(DataTemplate), typeof(AutoCompleteBox), new PropertyMetadata(null)); #endregion public DataTemplate ItemTemplate #region public Style ItemContainerStyle /// /// Gets or sets the that is /// applied to the selection adapter contained in the drop-down portion /// of the /// control. /// /// The applied to the /// selection adapter contained in the drop-down portion of the /// control. /// The default is null. /// /// The default selection adapter contained in the drop-down is a /// ListBox control. /// public Style ItemContainerStyle { get { return GetValue(ItemContainerStyleProperty) as Style; } set { SetValue(ItemContainerStyleProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register( ElementItemContainerStyle, typeof(Style), typeof(AutoCompleteBox), new PropertyMetadata(null, null)); #endregion public Style ItemContainerStyle #region public Style TextBoxStyle /// /// Gets or sets the applied to /// the text box portion of the /// control. /// /// The applied to the text /// box portion of the /// control. /// The default is null. public Style TextBoxStyle { get { return GetValue(TextBoxStyleProperty) as Style; } set { SetValue(TextBoxStyleProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty TextBoxStyleProperty = DependencyProperty.Register( ElementTextBoxStyle, typeof(Style), typeof(AutoCompleteBox), new PropertyMetadata(null)); #endregion public Style TextBoxStyle #region public double MaxDropDownHeight /// /// Gets or sets the maximum height of the drop-down portion of the /// control. /// /// The maximum height of the drop-down portion of the /// control. /// The default is . /// The specified value is less than 0. public double MaxDropDownHeight { get { return (double)GetValue(MaxDropDownHeightProperty); } set { SetValue(MaxDropDownHeightProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register( "MaxDropDownHeight", typeof(double), typeof(AutoCompleteBox), new PropertyMetadata(double.PositiveInfinity, OnMaxDropDownHeightPropertyChanged)); /// /// MaxDropDownHeightProperty property changed handler. /// /// AutoCompleteTextBox that changed its MaxDropDownHeight. /// Event arguments. [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The exception will be called through a CLR setter in most cases.")] private static void OnMaxDropDownHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; if (source._ignorePropertyChange) { source._ignorePropertyChange = false; return; } double newValue = (double)e.NewValue; // Revert to the old value if invalid (negative) if (newValue < 0) { source._ignorePropertyChange = true; source.SetValue(e.Property, e.OldValue); throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.AutoComplete_OnMaxDropDownHeightPropertyChanged_InvalidValue, e.NewValue), "value"); } source.OnMaxDropDownHeightChanged(newValue); } #endregion public double MaxDropDownHeight #region public bool IsDropDownOpen /// /// Gets or sets a value indicating whether the drop-down portion of /// the control is open. /// /// /// True if the drop-down is open; otherwise, false. The default is /// false. /// public bool IsDropDownOpen { get { return (bool)GetValue(IsDropDownOpenProperty); } set { SetValue(IsDropDownOpenProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register( "IsDropDownOpen", typeof(bool), typeof(AutoCompleteBox), new PropertyMetadata(false, OnIsDropDownOpenPropertyChanged)); /// /// IsDropDownOpenProperty property changed handler. /// /// AutoCompleteTextBox that changed its IsDropDownOpen. /// Event arguments. private static void OnIsDropDownOpenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; // Ignore the change if requested if (source._ignorePropertyChange) { source._ignorePropertyChange = false; return; } bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; if (newValue) { source.TextUpdated(source.Text, true); } else { source.ClosingDropDown(oldValue); } source.UpdateVisualState(true); } #endregion public bool IsDropDownOpen #region public IEnumerable ItemsSource /// /// Gets or sets a collection that is used to generate the items for the /// drop-down portion of the /// control. /// /// The collection that is used to generate the items of the /// drop-down portion of the /// control. public IEnumerable ItemsSource { get { return GetValue(ItemsSourceProperty) as IEnumerable; } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( "ItemsSource", typeof(IEnumerable), typeof(AutoCompleteBox), new PropertyMetadata(OnItemsSourcePropertyChanged)); /// /// ItemsSourceProperty property changed handler. /// /// AutoCompleteBox that changed its ItemsSource. /// Event arguments. private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox autoComplete = d as AutoCompleteBox; autoComplete.OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue); } #endregion public IEnumerable ItemsSource #region public object SelectedItem /// /// Gets or sets the selected item in the drop-down. /// /// The selected item in the drop-down. /// /// If the IsTextCompletionEnabled property is true and text typed by /// the user matches an item in the ItemsSource collection, which is /// then displayed in the text box, the SelectedItem property will be /// a null reference. /// public object SelectedItem { get { return GetValue(SelectedItemProperty) as object; } set { SetValue(SelectedItemProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier the /// /// dependency property. public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( "SelectedItem", typeof(object), typeof(AutoCompleteBox), new PropertyMetadata(OnSelectedItemPropertyChanged)); /// /// SelectedItemProperty property changed handler. Fires the /// SelectionChanged event. The event data will contain any non-null /// removed items and non-null additions. /// /// AutoCompleteBox that changed its SelectedItem. /// Event arguments. private static void OnSelectedItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; if (source._ignorePropertyChange) { source._ignorePropertyChange = false; return; } // Update the text display if (source._skipSelectedItemTextUpdate) { source._skipSelectedItemTextUpdate = false; } else { source.OnSelectedItemChanged(e.NewValue); } // Fire the SelectionChanged event List removed = new List(); if (e.OldValue != null) { removed.Add(e.OldValue); } List added = new List(); if (e.NewValue != null) { added.Add(e.NewValue); } source.OnSelectionChanged(new SelectionChangedEventArgs( SelectionChangedEvent, removed, added)); } /// /// Called when the selected item is changed, updates the text value /// that is displayed in the text box part. /// /// The new item. private void OnSelectedItemChanged(object newItem) { string text; if (newItem == null) { text = SearchText; } else { text = FormatValue(newItem, true); } // Update the Text property and the TextBox values UpdateTextValue(text); // Move the caret to the end of the text box if (TextBox != null && Text != null) { TextBox.SelectionStart = Text.Length; } } #endregion public object SelectedItem #region public string Text /// /// Gets or sets the text in the text box portion of the /// control. /// /// The text in the text box portion of the /// control. public string Text { get { return GetValue(TextProperty) as string; } set { SetValue(TextProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof(string), typeof(AutoCompleteBox), new PropertyMetadata(string.Empty, OnTextPropertyChanged)); /// /// TextProperty property changed handler. /// /// AutoCompleteBox that changed its Text. /// Event arguments. private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; source.TextUpdated((string)e.NewValue, false); } #endregion public string Text #region public string SearchText /// /// Gets the text that is used to filter items in the /// /// item collection. /// /// The text that is used to filter items in the /// /// item collection. /// /// The SearchText value is typically the same as the /// Text property, but is set after the TextChanged event occurs /// and before the Populating event. /// public string SearchText { get { return (string)GetValue(SearchTextProperty); } private set { try { _allowWrite = true; SetValue(SearchTextProperty, value); } finally { _allowWrite = false; } } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register( "SearchText", typeof(string), typeof(AutoCompleteBox), new PropertyMetadata(string.Empty, OnSearchTextPropertyChanged)); /// /// OnSearchTextProperty property changed handler. /// /// AutoCompleteBox that changed its SearchText. /// Event arguments. private static void OnSearchTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; if (source._ignorePropertyChange) { source._ignorePropertyChange = false; return; } // Ensure the property is only written when expected if (!source._allowWrite) { // Reset the old value before it was incorrectly written source._ignorePropertyChange = true; source.SetValue(e.Property, e.OldValue); throw new InvalidOperationException(Properties.Resources.AutoComplete_OnSearchTextPropertyChanged_InvalidWrite); } } #endregion public string SearchText #region public AutoCompleteFilterMode FilterMode /// /// Gets or sets how the text in the text box is used to filter items /// specified by the /// /// property for display in the drop-down. /// /// One of the /// /// values The default is /// . /// The specified value is /// not a valid /// . /// /// Use the FilterMode property to specify how possible matches are /// filtered. For example, possible matches can be filtered in a /// predefined or custom way. The search mode is automatically set to /// Custom if you set the ItemFilter property. /// public AutoCompleteFilterMode FilterMode { get { return (AutoCompleteFilterMode)GetValue(FilterModeProperty); } set { SetValue(FilterModeProperty, value); } } /// /// Gets the identifier for the /// /// dependency property. /// public static readonly DependencyProperty FilterModeProperty = DependencyProperty.Register( "FilterMode", typeof(AutoCompleteFilterMode), typeof(AutoCompleteBox), new PropertyMetadata(AutoCompleteFilterMode.StartsWith, OnFilterModePropertyChanged)); /// /// FilterModeProperty property changed handler. /// /// AutoCompleteBox that changed its FilterMode. /// Event arguments. [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The exception will be thrown when the CLR setter is used in most situations.")] private static void OnFilterModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue; if (mode != AutoCompleteFilterMode.Contains && mode != AutoCompleteFilterMode.ContainsCaseSensitive && mode != AutoCompleteFilterMode.ContainsOrdinal && mode != AutoCompleteFilterMode.ContainsOrdinalCaseSensitive && mode != AutoCompleteFilterMode.Custom && mode != AutoCompleteFilterMode.Equals && mode != AutoCompleteFilterMode.EqualsCaseSensitive && mode != AutoCompleteFilterMode.EqualsOrdinal && mode != AutoCompleteFilterMode.EqualsOrdinalCaseSensitive && mode != AutoCompleteFilterMode.None && mode != AutoCompleteFilterMode.StartsWith && mode != AutoCompleteFilterMode.StartsWithCaseSensitive && mode != AutoCompleteFilterMode.StartsWithOrdinal && mode != AutoCompleteFilterMode.StartsWithOrdinalCaseSensitive) { source.SetValue(e.Property, e.OldValue); throw new ArgumentException(Properties.Resources.AutoComplete_OnFilterModePropertyChanged_InvalidValue, "value"); } // Sets the filter predicate for the new value AutoCompleteFilterMode newValue = (AutoCompleteFilterMode)e.NewValue; source.TextFilter = AutoCompleteSearch.GetFilter(newValue); } #endregion public AutoCompleteFilterMode FilterMode #region public AutoCompleteFilterPredicate ItemFilter /// /// Gets or sets the custom method that uses user-entered text to filter /// the items specified by the /// /// property for display in the drop-down. /// /// The custom method that uses the user-entered text to filter /// the items specified by the /// /// property. The default is null. /// /// The filter mode is automatically set to Custom if you set the /// ItemFilter property. /// public AutoCompleteFilterPredicate ItemFilter { get { return GetValue(ItemFilterProperty) as AutoCompleteFilterPredicate; } set { SetValue(ItemFilterProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty ItemFilterProperty = DependencyProperty.Register( "ItemFilter", typeof(AutoCompleteFilterPredicate), typeof(AutoCompleteBox), new PropertyMetadata(OnItemFilterPropertyChanged)); /// /// ItemFilterProperty property changed handler. /// /// AutoCompleteBox that changed its ItemFilter. /// Event arguments. private static void OnItemFilterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; AutoCompleteFilterPredicate value = e.NewValue as AutoCompleteFilterPredicate; // If null, revert to the "None" predicate if (value == null) { source.FilterMode = AutoCompleteFilterMode.None; } else { source.FilterMode = AutoCompleteFilterMode.Custom; source.TextFilter = null; } } #endregion public AutoCompleteFilterPredicate ItemFilter #region public AutoCompleteStringFilterPredicate TextFilter /// /// Gets or sets the custom method that uses the user-entered text to /// filter items specified by the /// /// property in a text-based way for display in the drop-down. /// /// The custom method that uses the user-entered text to filter /// items specified by the /// /// property in a text-based way for display in the drop-down. /// /// The search mode is automatically set to Custom if you set the /// TextFilter property. /// public AutoCompleteFilterPredicate TextFilter { get { return GetValue(TextFilterProperty) as AutoCompleteFilterPredicate; } set { SetValue(TextFilterProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. public static readonly DependencyProperty TextFilterProperty = DependencyProperty.Register( "TextFilter", typeof(AutoCompleteFilterPredicate), typeof(AutoCompleteBox), new PropertyMetadata(AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith))); #endregion public AutoCompleteStringFilterPredicate TextFilter #region public object Placeholder /// /// Gets or sets the content of the placeholder which is overlaid over /// the textbox when it is empty. /// /// The content of the placeholder which is overlaid over the textbox /// when it is empty. public object Placeholder { get { return GetValue(PlaceholderProperty); } set { SetValue(PlaceholderProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. /// public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.Register( "Placeholder", typeof(object), typeof(AutoCompleteBox)); #endregion #region public DataTemplate PlaceholderTemplate /// /// Gets or sets the used /// to display the placeholder of the control. /// /// The used to /// display the placeholder of the control. /// /// You use the PlaceholderTemplate property to specify the visualization /// of the data objects in the Placeholder of the AutoCompleteBox /// control. /// public DataTemplate PlaceholderTemplate { get { return (DataTemplate)GetValue(PlaceholderTemplateProperty); } set { SetValue(PlaceholderTemplateProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. /// public static readonly DependencyProperty PlaceholderTemplateProperty = DependencyProperty.Register( "PlaceholderTemplate", typeof(DataTemplate), typeof(AutoCompleteBox)); #endregion #region public DataTemplateSelector PlaceholderTemplateSelector /// /// Gets or sets the used /// to display the placeholder of the control. /// /// The used to /// display the placeholder of the control. /// /// You use the PlaceholderTemplateSelector property to add custom logic to decide which /// template to apply to the AutoCompleteBox' placeholder. /// Please note that the PlaceholderTemplateSelector is only used if the PlaceholderTemplate /// is set to null. /// public DataTemplateSelector PlaceholderTemplateSelector { get { return (DataTemplateSelector)GetValue(PlaceholderTemplateSelectorProperty); } set { SetValue(PlaceholderTemplateSelectorProperty, value); } } /// /// Identifies the /// /// dependency property. /// /// The identifier for the /// /// dependency property. /// public static readonly DependencyProperty PlaceholderTemplateSelectorProperty = DependencyProperty.Register( "PlaceholderTemplateSelector", typeof(DataTemplateSelector), typeof(AutoCompleteBox)); #endregion #region Template parts /// /// Gets or sets the drop down popup control. /// private PopupHelper DropDownPopup { get; set; } /// /// The TextBox template part. /// private TextBox _text; /// /// The SelectionAdapter. /// private ISelectionAdapter _adapter; /// /// Gets or sets the Text template part. /// internal TextBox TextBox { get { return _text; } set { // Detach existing handlers if (_text != null) { _text.SelectionChanged -= OnTextBoxSelectionChanged; _text.TextChanged -= OnTextBoxTextChanged; } _text = value; // Attach handlers if (_text != null) { _text.SelectionChanged += OnTextBoxSelectionChanged; _text.TextChanged += OnTextBoxTextChanged; if (Text != null) { UpdateTextValue(Text); } } } } /// /// Gets or sets the selection adapter used to populate the drop-down /// with a list of selectable items. /// /// The selection adapter used to populate the drop-down with a /// list of selectable items. /// /// You can use this property when you create an automation peer to /// use with AutoCompleteBox or deriving from AutoCompleteBox to /// create a custom control. /// protected internal ISelectionAdapter SelectionAdapter { get { return _adapter; } set { if (_adapter != null) { _adapter.SelectionChanged -= OnAdapterSelectionChanged; _adapter.Commit -= OnAdapterSelectionComplete; _adapter.Cancel -= OnAdapterSelectionCanceled; _adapter.Cancel -= OnAdapterSelectionComplete; _adapter.ItemsSource = null; } _adapter = value; if (_adapter != null) { _adapter.SelectionChanged += OnAdapterSelectionChanged; _adapter.Commit += OnAdapterSelectionComplete; _adapter.Cancel += OnAdapterSelectionCanceled; _adapter.Cancel += OnAdapterSelectionComplete; _adapter.ItemsSource = _view; } } } #endregion /// /// Occurs when the text in the text box portion of the /// changes. /// public static readonly RoutedEvent TextChangedEvent = EventManager.RegisterRoutedEvent("TextChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the text in the text box portion of the /// changes. /// public event RoutedEventHandler TextChanged { add { AddHandler(TextChangedEvent, value); } remove { RemoveHandler(TextChangedEvent, value); } } /// /// Occurs when the /// is /// populating the drop-down with possible matches based on the /// /// property. /// /// /// If the event is canceled, by setting the PopulatingEventArgs.Cancel /// property to true, the AutoCompleteBox will not automatically /// populate the selection adapter contained in the drop-down. /// In this case, if you want possible matches to appear, you must /// provide the logic for populating the selection adapter. /// public static readonly RoutedEvent PopulatingEvent = EventManager.RegisterRoutedEvent("Populating", RoutingStrategy.Bubble, typeof(PopulatingEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the /// is /// populating the drop-down with possible matches based on the /// /// property. /// /// /// If the event is canceled, by setting the PopulatingEventArgs.Cancel /// property to true, the AutoCompleteBox will not automatically /// populate the selection adapter contained in the drop-down. /// In this case, if you want possible matches to appear, you must /// provide the logic for populating the selection adapter. /// public event PopulatingEventHandler Populating { add { AddHandler(PopulatingEvent, value); } remove { RemoveHandler(PopulatingEvent, value); } } /// /// Occurs when the /// has /// populated the drop-down with possible matches based on the /// /// property. /// public static readonly RoutedEvent PopulatedEvent = EventManager.RegisterRoutedEvent("Populated", RoutingStrategy.Bubble, typeof(PopulatedEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the /// has /// populated the drop-down with possible matches based on the /// /// property. /// public event PopulatedEventHandler Populated { add { AddHandler(PopulatedEvent, value); } remove { RemoveHandler(PopulatedEvent, value); } } /// /// Occurs when the value of the /// /// property is changing from false to true. /// public static readonly RoutedEvent DropDownOpeningEvent = EventManager.RegisterRoutedEvent("DropDownOpening", RoutingStrategy.Bubble, typeof(RoutedPropertyChangingEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the value of the /// /// property is changing from false to true. /// public event RoutedPropertyChangingEventHandler DropDownOpening { add { AddHandler(DropDownOpeningEvent, value); } remove { RemoveHandler(DropDownOpeningEvent, value); } } /// /// Occurs when the value of the /// /// property has changed from false to true and the drop-down is open. /// public static readonly RoutedEvent DropDownOpenedEvent = EventManager.RegisterRoutedEvent("DropDownOpened", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the value of the /// /// property has changed from false to true and the drop-down is open. /// public event RoutedPropertyChangedEventHandler DropDownOpened { add { AddHandler(DropDownOpenedEvent, value); } remove { RemoveHandler(DropDownOpenedEvent, value); } } /// /// Occurs when the /// /// property is changing from true to false. /// public static readonly RoutedEvent DropDownClosingEvent = EventManager.RegisterRoutedEvent("DropDownClosing", RoutingStrategy.Bubble, typeof(RoutedPropertyChangingEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the /// /// property is changing from true to false. /// public event RoutedPropertyChangingEventHandler DropDownClosing { add { AddHandler(DropDownClosingEvent, value); } remove { RemoveHandler(DropDownClosingEvent, value); } } /// /// Occurs when the /// /// property was changed from true to false and the drop-down is open. /// public static readonly RoutedEvent DropDownClosedEvent = EventManager.RegisterRoutedEvent("DropDownClosed", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the /// /// property was changed from true to false and the drop-down is open. /// public event RoutedPropertyChangedEventHandler DropDownClosed { add { AddHandler(DropDownClosedEvent, value); } remove { RemoveHandler(DropDownClosedEvent, value); } } /// /// Occurs when the selected item in the drop-down portion of the /// has /// changed. /// public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(AutoCompleteBox)); /// /// Occurs when the selected item in the drop-down portion of the /// has /// changed. /// public event SelectionChangedEventHandler SelectionChanged { add { AddHandler(SelectionChangedEvent, value); } remove { RemoveHandler(SelectionChangedEvent, value); } } private Binding _valueMemberBinding; /// /// Gets or sets the that /// is used to get the values for display in the text portion of /// the /// control. /// /// The object used /// when binding to a collection property. public Binding ValueMemberBinding { get { return _valueMemberBinding; } set { _valueMemberBinding = value; if (_valueMemberBinding != null && _valueMemberBinding.Path != null) { ValueMemberPath = _valueMemberBinding.Path.Path; } else { ValueMemberPath = null; } } } /// /// Gets or sets the property path that is used to get values for /// display in the text portion of the /// control. /// /// The property path that is used to get values for display in /// the text portion of the /// control. public string ValueMemberPath { get { return (string)GetValue(ValueMemberPathProperty); } set { SetValue(ValueMemberPathProperty, value); } } public static readonly DependencyProperty ValueMemberPathProperty = DependencyProperty.Register("ValueMemberPath", typeof(string), typeof(AutoCompleteBox), new PropertyMetadata(null, ValueMemberPathChanged)); /// /// Handle the change of the ValueMemberPath property. /// /// The source object. /// The event data. private static void ValueMemberPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteBox source = d as AutoCompleteBox; source.OnSelectedItemChanged(source.SelectedItem); } #if !SILVERLIGHT /// /// Initializes the static members of the /// class. /// static AutoCompleteBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoCompleteBox), new FrameworkPropertyMetadata(typeof(AutoCompleteBox))); } #endif /// /// Initializes a new instance of the /// class. /// public AutoCompleteBox() { #if SILVERLIGHT DefaultStyleKey = typeof(AutoCompleteBox); Loaded += (sender, e) => ApplyTemplate(); #endif IsEnabledChanged += ControlIsEnabledChanged; Interaction = new InteractionHelper(this); // Creating the view here ensures that View is always != null ClearView(); } /// /// Arranges and sizes the /// /// control and its contents. /// /// The size allowed for the /// control. /// The , unchanged. protected override Size ArrangeOverride(Size finalSize) { Size r = base.ArrangeOverride(finalSize); if (DropDownPopup != null) { DropDownPopup.Arrange(); } return r; } /// /// Builds the visual tree for the /// control /// when a new template is applied. /// public override void OnApplyTemplate() { #if !SILVERLIGHT if (TextBox != null) { TextBox.PreviewKeyDown -= OnTextBoxPreviewKeyDown; } #endif if (DropDownPopup != null) { DropDownPopup.Closed -= DropDownPopup_Closed; DropDownPopup.FocusChanged -= OnDropDownFocusChanged; DropDownPopup.UpdateVisualStates -= OnDropDownPopupUpdateVisualStates; DropDownPopup.BeforeOnApplyTemplate(); DropDownPopup = null; } base.OnApplyTemplate(); // Set the template parts. Individual part setters remove and add // any event handlers. Popup popup = GetTemplateChild(ElementPopup) as Popup; if (popup != null) { DropDownPopup = new PopupHelper(this, popup); DropDownPopup.MaxDropDownHeight = MaxDropDownHeight; DropDownPopup.AfterOnApplyTemplate(); DropDownPopup.Closed += DropDownPopup_Closed; DropDownPopup.FocusChanged += OnDropDownFocusChanged; DropDownPopup.UpdateVisualStates += OnDropDownPopupUpdateVisualStates; } SelectionAdapter = GetSelectionAdapterPart(); TextBox = GetTemplateChild(AutoCompleteBox.ElementTextBox) as TextBox; #if !SILVERLIGHT if (TextBox != null) { TextBox.PreviewKeyDown += OnTextBoxPreviewKeyDown; } #endif Interaction.OnApplyTemplateBase(); // If the drop down property indicates that the popup is open, // flip its value to invoke the changed handler. if (IsDropDownOpen && DropDownPopup != null && !DropDownPopup.IsOpen) { OpeningDropDown(false); } } /// /// Allows the popup wrapper to fire visual state change events. /// /// The source object. /// The event data. private void OnDropDownPopupUpdateVisualStates(object sender, EventArgs e) { UpdateVisualState(true); } /// /// Allows the popup wrapper to fire the FocusChanged event. /// /// The source object. /// The event data. private void OnDropDownFocusChanged(object sender, EventArgs e) { FocusChanged(HasFocus()); } /// /// Begin closing the drop-down. /// /// The original value. private void ClosingDropDown(bool oldValue) { bool delayedClosingVisual = false; if (DropDownPopup != null) { delayedClosingVisual = DropDownPopup.UsesClosingVisualState; } #if SILVERLIGHT RoutedPropertyChangingEventArgs args = new RoutedPropertyChangingEventArgs(IsDropDownOpenProperty, oldValue, false, true); #else RoutedPropertyChangingEventArgs args = new RoutedPropertyChangingEventArgs(IsDropDownOpenProperty, oldValue, false, true, DropDownClosingEvent); #endif OnDropDownClosing(args); if (_view == null || _view.Count == 0) { delayedClosingVisual = false; } if (args.Cancel) { _ignorePropertyChange = true; SetValue(IsDropDownOpenProperty, oldValue); } else { // Immediately close the drop down window: // When a popup closed visual state is present, the code path is // slightly different and the actual call to CloseDropDown will // be called only after the visual state's transition is done RaiseExpandCollapseAutomationEvent(oldValue, false); if (!delayedClosingVisual) { CloseDropDown(oldValue, false); } } UpdateVisualState(true); } /// /// Begin opening the drop down by firing cancelable events, opening the /// drop-down or reverting, depending on the event argument values. /// /// The original value, if needed for a revert. private void OpeningDropDown(bool oldValue) { #if SILVERLIGHT RoutedPropertyChangingEventArgs args = new RoutedPropertyChangingEventArgs(IsDropDownOpenProperty, oldValue, true, true); #else RoutedPropertyChangingEventArgs args = new RoutedPropertyChangingEventArgs(IsDropDownOpenProperty, oldValue, true, true, DropDownOpeningEvent); #endif // Opening OnDropDownOpening(args); if (args.Cancel) { _ignorePropertyChange = true; SetValue(IsDropDownOpenProperty, oldValue); } else { RaiseExpandCollapseAutomationEvent(oldValue, true); OpenDropDown(oldValue, true); } UpdateVisualState(true); } /// /// Raise an expand/collapse event through the automation peer. /// /// The old value. /// The new value. private void RaiseExpandCollapseAutomationEvent(bool oldValue, bool newValue) { AutoCompleteBoxAutomationPeer peer = FrameworkElementAutomationPeer.FromElement(this) as AutoCompleteBoxAutomationPeer; if (peer != null) { peer.RaiseExpandCollapseAutomationEvent(oldValue, newValue); } } /// /// Handles the PreviewKeyDown event on the TextBox for WPF. /// /// The source object. /// The event data. private void OnTextBoxPreviewKeyDown(object sender, KeyEventArgs e) { OnKeyDown(e); } /// /// Connects to the DropDownPopup Closed event. /// /// The source object. /// The event data. private void DropDownPopup_Closed(object sender, EventArgs e) { // Force the drop down dependency property to be false. if (IsDropDownOpen) { IsDropDownOpen = false; } // Fire the DropDownClosed event if (_popupHasOpened) { #if SILVERLIGHT OnDropDownClosed(new RoutedPropertyChangedEventArgs(true, false)); #else OnDropDownClosed(new RoutedPropertyChangedEventArgs(true, false, DropDownClosedEvent)); #endif } } /// /// Creates an /// /// /// A /// /// for the /// object. protected override AutomationPeer OnCreateAutomationPeer() { return new AutoCompleteBoxAutomationPeer(this); } #region Focus /// /// Handles the FocusChanged event. /// /// A value indicating whether the control /// currently has the focus. private void FocusChanged(bool hasFocus) { // The OnGotFocus & OnLostFocus are asynchronously and cannot // reliably tell you that have the focus. All they do is let you // know that the focus changed sometime in the past. To determine // if you currently have the focus you need to do consult the // FocusManager (see HasFocus()). if (hasFocus) { if (TextBox != null && TextBox.SelectionLength == 0) { TextBox.SelectAll(); } } else { IsDropDownOpen = false; _userCalledPopulate = false; if (TextBox != null) { TextBox.Select(TextBox.Text.Length, 0); } } } /// /// Determines whether the text box or drop-down portion of the /// control has /// focus. /// /// true to indicate the /// has focus; /// otherwise, false. protected bool HasFocus() { DependencyObject focused = #if SILVERLIGHT FocusManager.GetFocusedElement() as DependencyObject; #else // For WPF, check if the element that has focus is within the control, as // FocusManager.GetFocusedElement(this) will return null in such a case. this.IsKeyboardFocusWithin ? Keyboard.FocusedElement as DependencyObject : FocusManager.GetFocusedElement(this) as DependencyObject; #endif while (focused != null) { if (object.ReferenceEquals(focused, this)) { return true; } // This helps deal with popups that may not be in the same // visual tree DependencyObject parent = VisualTreeHelper.GetParent(focused); if (parent == null) { // Try the logical parent. FrameworkElement element = focused as FrameworkElement; if (element != null) { parent = element.Parent; } } focused = parent; } return false; } /// /// Provides handling for the /// event. /// /// A /// that contains the event data. protected override void OnGotFocus(RoutedEventArgs e) { base.OnGotFocus(e); FocusChanged(HasFocus()); if (TextBox != null) TextBox.Focus(); } #if !SILVERLIGHT /// /// Handles change of keyboard focus, which is treated differently than control focus /// /// protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e) { base.OnIsKeyboardFocusWithinChanged(e); FocusChanged((bool)e.NewValue); } #endif /// /// Provides handling for the /// event. /// /// A /// that contains the event data. protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); FocusChanged(HasFocus()); } #endregion /// /// Handle the change of the IsEnabled property. /// /// The source object. /// The event data. private void ControlIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { bool isEnabled = (bool)e.NewValue; if (!isEnabled) { IsDropDownOpen = false; } } /// /// Returns the /// part, if /// possible. /// /// /// A object, /// if possible. Otherwise, null. /// [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Following the GetTemplateChild pattern for the method.")] protected virtual ISelectionAdapter GetSelectionAdapterPart() { ISelectionAdapter adapter = null; Selector selector = GetTemplateChild(ElementSelector) as Selector; if (selector != null) { // Check if it is already an IItemsSelector adapter = selector as ISelectionAdapter; if (adapter == null) { // Built in support for wrapping a Selector control adapter = new SelectorSelectionAdapter(selector); } } if (adapter == null) { adapter = GetTemplateChild(ElementSelectionAdapter) as ISelectionAdapter; } return adapter; } /// /// Handles the timer tick when using a populate delay. /// /// The source object. /// The event arguments. private void PopulateDropDown(object sender, EventArgs e) { if (_delayTimer != null) { _delayTimer.Stop(); } // Update the prefix/search text. SearchText = Text; // The Populated event enables advanced, custom filtering. The // client needs to directly update the ItemsSource collection or // call the Populate method on the control to continue the // display process if Cancel is set to true. #if SILVERLIGHT PopulatingEventArgs populating = new PopulatingEventArgs(SearchText); #else PopulatingEventArgs populating = new PopulatingEventArgs(SearchText, PopulatingEvent); #endif OnPopulating(populating); if (!populating.Cancel) { PopulateComplete(); } } /// /// Raises the /// /// event. /// /// A /// that /// contains the event data. protected virtual void OnPopulating(PopulatingEventArgs e) { #if SILVERLIGHT PopulatingEventHandler handler = Populating; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Raises the /// /// event. /// /// A /// /// that contains the event data. protected virtual void OnPopulated(PopulatedEventArgs e) { #if SILVERLIGHT PopulatedEventHandler handler = Populated; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Raises the /// /// event. /// /// A /// /// that contains the event data. protected virtual void OnSelectionChanged(SelectionChangedEventArgs e) { #if SILVERLIGHT SelectionChangedEventHandler handler = SelectionChanged; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Raises the /// /// event. /// /// A /// /// that contains the event data. protected virtual void OnDropDownOpening(RoutedPropertyChangingEventArgs e) { #if SILVERLIGHT RoutedPropertyChangingEventHandler handler = DropDownOpening; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Raises the /// /// event. /// /// A /// /// that contains the event data. protected virtual void OnDropDownOpened(RoutedPropertyChangedEventArgs e) { #if SILVERLIGHT RoutedPropertyChangedEventHandler handler = DropDownOpened; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Raises the /// /// event. /// /// A /// /// that contains the event data. protected virtual void OnDropDownClosing(RoutedPropertyChangingEventArgs e) { RaiseEvent(e); } /// /// Raises the /// /// event. /// /// A /// /// which contains the event data. protected virtual void OnDropDownClosed(RoutedPropertyChangedEventArgs e) { RaiseEvent(e); } /// /// Formats an Item for text comparisons based on Converter /// and ConverterCulture properties. /// /// The object to format. /// A value indicating whether to clear /// the data context after the lookup is performed. /// Formatted Value. private string FormatValue(object value, bool clearDataContext) { return FormatValue(value); } /// /// Converts the specified object to a string by using the /// and /// values /// of the binding object specified by the /// /// property. /// /// The object to format as a string. /// The string representation of the specified object. /// /// Override this method to provide a custom string conversion. /// protected virtual string FormatValue(object value) { if (ValueMemberPath != null) { var readValue = ValueByStringHelper.GetStringValue(ValueMemberPath, _valueMemberBinding, value); return readValue != null ? readValue : string.Empty; } return value == null ? string.Empty : value.ToString(); } /// /// Raises the /// /// event. /// /// A /// that contains the event data. protected virtual void OnTextChanged(RoutedEventArgs e) { #if SILVERLIGHT RoutedEventHandler handler = TextChanged; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Handle the TextChanged event that is directly attached to the /// TextBox part. This ensures that only user initiated actions will /// result in an AutoCompleteBox suggestion and operation. /// /// The source TextBox object. /// The TextChanged event data. private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e) { // Call the central updated text method as a user-initiated action TextUpdated(_text.Text, true); } /// /// When selection changes, save the location of the selection start. /// /// The source object. /// The event data. private void OnTextBoxSelectionChanged(object sender, RoutedEventArgs e) { // If ignoring updates. This happens after text is updated, and // before the PopulateComplete method is called. Required for the // IsTextCompletionEnabled feature. if (_ignoreTextSelectionChange) { return; } _textSelectionStart = _text.SelectionStart; } /// /// Updates both the text box value and underlying text dependency /// property value if and when they change. Automatically fires the /// text changed events when there is a change. /// /// The new string value. private void UpdateTextValue(string value) { UpdateTextValue(value, null); } /// /// Updates both the text box value and underlying text dependency /// property value if and when they change. Automatically fires the /// text changed events when there is a change. /// /// The new string value. /// A nullable bool value indicating whether /// the action was user initiated. In a user initiated mode, the /// underlying text dependency property is updated. In a non-user /// interaction, the text box value is updated. When user initiated is /// null, all values are updated. private void UpdateTextValue(string value, bool? userInitiated) { // Update the Text dependency property if ((userInitiated == null || userInitiated == true) && Text != value) { _ignoreTextPropertyChange++; Text = value; #if SILVERLIGHT OnTextChanged(new RoutedEventArgs()); #else OnTextChanged(new RoutedEventArgs(TextChangedEvent)); #endif } // Update the TextBox's Text dependency property if ((userInitiated == null || userInitiated == false) && TextBox != null && TextBox.Text != value) { _ignoreTextPropertyChange++; TextBox.Text = value ?? string.Empty; // Text dependency property value was set, fire event if (Text == value || Text == null) { #if SILVERLIGHT OnTextChanged(new RoutedEventArgs()); #else OnTextChanged(new RoutedEventArgs(TextChangedEvent)); #endif } } } /// /// Handle the update of the text for the control from any source, /// including the TextBox part and the Text dependency property. /// /// The new text. /// A value indicating whether the update /// is a user-initiated action. This should be a True value when the /// TextUpdated method is called from a TextBox event handler. private void TextUpdated(string newText, bool userInitiated) { // Only process this event if it is coming from someone outside // setting the Text dependency property directly. if (_ignoreTextPropertyChange > 0) { _ignoreTextPropertyChange--; return; } if (newText == null) { newText = string.Empty; } // The TextBox.TextChanged event was not firing immediately and // was causing an immediate update, even with wrapping. If there is // a selection currently, no update should happen. if (IsTextCompletionEnabled && TextBox != null && TextBox.SelectionLength > 0 && TextBox.SelectionStart != TextBox.Text.Length) { return; } // Evaluate the conditions needed for completion. // 1. Minimum prefix length // 2. If a delay timer is in use, use it bool populateReady = newText.Length >= MinimumPrefixLength && MinimumPrefixLength >= 0; _userCalledPopulate = populateReady ? userInitiated : false; // Update the interface and values only as necessary UpdateTextValue(newText, userInitiated); if (populateReady) { _ignoreTextSelectionChange = true; if (_delayTimer != null) { _delayTimer.Start(); } else { PopulateDropDown(this, EventArgs.Empty); } } else { SearchText = string.Empty; if (SelectedItem != null) { _skipSelectedItemTextUpdate = true; } SelectedItem = null; if (IsDropDownOpen) { IsDropDownOpen = false; } } } /// /// Notifies the /// that the /// /// property has been set and the data can be filtered to provide /// possible matches in the drop-down. /// /// /// Call this method when you are providing custom population of /// the drop-down portion of the AutoCompleteBox, to signal the control /// that you are done with the population process. /// Typically, you use PopulateComplete when the population process /// is a long-running process and you want to cancel built-in filtering /// of the ItemsSource items. In this case, you can handle the /// Populated event and set PopulatingEventArgs.Cancel to true. /// When the long-running process has completed you call /// PopulateComplete to indicate the drop-down is populated. /// public void PopulateComplete() { // Apply the search filter RefreshView(); // Fire the Populated event containing the read-only view data. #if SILVERLIGHT PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection(_view)); #else PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection(_view), PopulatedEvent); #endif OnPopulated(populated); if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view) { SelectionAdapter.ItemsSource = _view; } bool isDropDownOpen = _userCalledPopulate && (_view.Count > 0); if (isDropDownOpen != IsDropDownOpen) { _ignorePropertyChange = true; IsDropDownOpen = isDropDownOpen; } if (IsDropDownOpen) { OpeningDropDown(false); if (DropDownPopup != null) { DropDownPopup.Arrange(); } } else { ClosingDropDown(true); } UpdateTextCompletion(_userCalledPopulate); } private void UpdateTextCompletion(object selectedItem) { if (SelectedItem != selectedItem) { _skipSelectedItemTextUpdate = true; } SelectedItem = selectedItem; if (SelectionAdapter != null) { SelectionAdapter.SelectedItem = selectedItem; } // Restore updates for TextSelection if (_ignoreTextSelectionChange) { _ignoreTextSelectionChange = false; if (TextBox != null) { _textSelectionStart = TextBox.SelectionStart; } } } /// /// Performs text completion, if enabled, and a lookup on the underlying /// item values for an exact match. Will update the SelectedItem value. /// /// A value indicating whether the operation /// was user initiated. Text completion will not be performed when not /// directly initiated by the user. private void UpdateTextCompletion(bool userInitiated) { // By default this method will clear the selected value object newSelectedItem = null; string text = Text; // Text search is StartsWith explicit and only when enabled, in // line with WPF's ComboBox lookup. When in use it will associate // a Value with the Text if it is found in ItemsSource. This is // only valid when there is data and the user initiated the action. if (_view.Count > 0) { if (IsTextCompletionEnabled && TextBox != null && userInitiated) { int currentLength = TextBox.Text.Length; int selectionStart = TextBox.SelectionStart; if (selectionStart == text.Length && selectionStart > _textSelectionStart) { // When the FilterMode dependency property is set to // either StartsWith or StartsWithCaseSensitive, the // first item in the view is used. This will improve // performance on the lookup. It assumes that the // FilterMode the user has selected is an acceptable // case sensitive matching function for their scenario. object top = FilterMode == AutoCompleteFilterMode.StartsWith || FilterMode == AutoCompleteFilterMode.StartsWithCaseSensitive ? _view[0] : TryGetMatch(text, SelectedItem, _view, AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); // If the search was successful, update SelectedItem if (top != null) { newSelectedItem = top; string topString = FormatValue(top, true); // Only replace partially when the two words being the same int minLength = Math.Min(topString.Length, Text.Length); if (AutoCompleteSearch.Equals(Text.Substring(0, minLength), topString.Substring(0, minLength))) { // Update the text UpdateTextValue(topString); // Select the text past the user's caret TextBox.SelectionStart = currentLength; TextBox.SelectionLength = topString.Length - currentLength; } } } } else { // Perform an exact string lookup for the text. This is a // design change from the original Toolkit release when the // IsTextCompletionEnabled property behaved just like the // WPF ComboBox's IsTextSearchEnabled property. // // This change provides the behavior that most people expect // to find: a lookup for the value is always performed. newSelectedItem = TryGetMatch(text, SelectedItem, _view, AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.EqualsCaseSensitive)); } } // Update the selected item property UpdateTextCompletion(newSelectedItem); } /// /// Attempts to look through the view and locate the specific exact /// text match. /// /// The search text. /// The view reference with priorty (check before the rest of views). /// The view reference. /// The predicate to use for the partial or /// exact match. /// Returns the object or null. private object TryGetMatch(string searchText, object priorityView,ObservableCollection view, AutoCompleteFilterPredicate predicate) { if (priorityView != null) { if (predicate(searchText, FormatValue(priorityView))) { return priorityView; } } if (view != null && view.Count > 0) { foreach (object o in view) { if (predicate(searchText, FormatValue(o))) { return o; } } } return null; } /// /// A simple helper method to clear the view and ensure that a view /// object is always present and not null. /// private void ClearView() { if (_view == null) { _view = new ObservableCollection(); } else { _view.Clear(); } } /// /// Walks through the items enumeration. Performance is not going to be /// perfect with the current implementation. /// private void RefreshView() { if (_items == null) { ClearView(); return; } // Cache the current text value string text = Text ?? string.Empty; // Determine if any filtering mode is on bool stringFiltering = TextFilter != null; bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null; int view_index = 0; int view_count = _view.Count; List items = _items; foreach (object item in items) { bool inResults = !(stringFiltering || objectFiltering); if (!inResults) { inResults = stringFiltering ? TextFilter(text, FormatValue(item)) : ItemFilter(text, item); } if (view_count > view_index && inResults && _view[view_index] == item) { // Item is still in the view view_index++; } else if (inResults) { // Insert the item if (view_count > view_index && _view[view_index] != item) { // Replace item // Unfortunately replacing via index throws a fatal // exception: View[view_index] = item; // Cost: O(n) vs O(1) _view.RemoveAt(view_index); _view.Insert(view_index, item); view_index++; } else { // Add the item if (view_index == view_count) { // Constant time is preferred (Add). _view.Add(item); } else { _view.Insert(view_index, item); } view_index++; view_count++; } } else if (view_count > view_index && _view[view_index] == item) { // Remove the item _view.RemoveAt(view_index); view_count--; } } } /// /// Handle any change to the ItemsSource dependency property, update /// the underlying ObservableCollection view, and set the selection /// adapter's ItemsSource to the view if appropriate. /// /// The old enumerable reference. /// The new enumerable reference. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "This makes it easy to add validation or other changes in the future.")] private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { // Remove handler for oldValue.CollectionChanged (if present) INotifyCollectionChanged oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged; if (null != oldValueINotifyCollectionChanged && null != _collectionChangedWeakEventListener) { _collectionChangedWeakEventListener.Detach(); _collectionChangedWeakEventListener = null; } // Add handler for newValue.CollectionChanged (if possible) INotifyCollectionChanged newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged; if (null != newValueINotifyCollectionChanged) { _collectionChangedWeakEventListener = new WeakEventListener(this); _collectionChangedWeakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs); _collectionChangedWeakEventListener.OnDetachAction = (weakEventListener) => newValueINotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent; newValueINotifyCollectionChanged.CollectionChanged += _collectionChangedWeakEventListener.OnEvent; } // Store a local cached copy of the data _items = newValue == null ? null : new List(newValue.Cast().ToList()); // Clear and set the view on the selection adapter ClearView(); if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view) { SelectionAdapter.ItemsSource = _view; } if (IsDropDownOpen) { RefreshView(); } } /// /// Method that handles the ObservableCollection.CollectionChanged event for the ItemsSource property. /// /// The object that raised the event. /// The event data. private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Update the cache if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null) { for (int index = 0; index < e.OldItems.Count; index++) { _items.RemoveAt(e.OldStartingIndex); } } if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null && _items.Count >= e.NewStartingIndex) { for (int index = 0; index < e.NewItems.Count; index++) { _items.Insert(e.NewStartingIndex + index, e.NewItems[index]); } } if (e.Action == NotifyCollectionChangedAction.Replace && e.NewItems != null && e.OldItems != null) { for (int index = 0; index < e.NewItems.Count; index++) { _items[e.NewStartingIndex] = e.NewItems[index]; } } // Update the view if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace) { for (int index = 0; index < e.OldItems.Count; index++) { _view.Remove(e.OldItems[index]); } } if (e.Action == NotifyCollectionChangedAction.Reset) { // Significant changes to the underlying data. ClearView(); if (ItemsSource != null) { _items = new List(ItemsSource.Cast().ToList()); } } // Refresh the observable collection used in the selection adapter. RefreshView(); } #region Selection Adapter /// /// Handles the SelectionChanged event of the selection adapter. /// /// The source object. /// The selection changed event data. private void OnAdapterSelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.AddedItems.Count == 1) { SelectedItem = e.AddedItems[0]; } else { SelectedItem = null; } } /// /// Handles the Commit event on the selection adapter. /// /// The source object. /// The event data. private void OnAdapterSelectionComplete(object sender, RoutedEventArgs e) { IsDropDownOpen = false; ISelectionAdapter selectionAdapter = (sender as ISelectionAdapter); if (selectionAdapter != null) { UpdateTextCompletion(selectionAdapter.SelectedItem); } else { UpdateTextCompletion(false); } // Text should not be selected if (TextBox != null) { TextBox.Select(TextBox.Text.Length, 0); } #if SILVERLIGHT Focus(); #else // Focus is treated differently in SL and WPF. // This forces the textbox to get keyboard focus, in the case where // another part of the control may have temporarily received focus. if (TextBox != null) { Keyboard.Focus(TextBox); } else { Focus(); } #endif } /// /// Handles the Cancel event on the selection adapter. /// /// The source object. /// The event data. private void OnAdapterSelectionCanceled(object sender, RoutedEventArgs e) { UpdateTextValue(SearchText); // Completion will update the selected value UpdateTextCompletion(false); } #endregion #region Popup /// /// Handles MaxDropDownHeightChanged by re-arranging and updating the /// popup arrangement. /// /// The new value. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newValue", Justification = "This makes it easy to add validation or other changes in the future.")] private void OnMaxDropDownHeightChanged(double newValue) { if (DropDownPopup != null) { DropDownPopup.MaxDropDownHeight = newValue; DropDownPopup.Arrange(); } UpdateVisualState(true); } /// /// Private method that directly opens the popup, checks the expander /// button, and then fires the Opened event. /// /// The old value. /// The new value. private void OpenDropDown(bool oldValue, bool newValue) { if (DropDownPopup != null) { DropDownPopup.IsOpen = true; } _popupHasOpened = true; #if SILVERLIGHT OnDropDownOpened(new RoutedPropertyChangedEventArgs(oldValue, newValue)); #else OnDropDownOpened(new RoutedPropertyChangedEventArgs(oldValue, newValue, DropDownOpenedEvent)); #endif } /// /// Private method that directly closes the popup, flips the Checked /// value, and then fires the Closed event. /// /// The old value. /// The new value. private void CloseDropDown(bool oldValue, bool newValue) { if (_popupHasOpened) { if (SelectionAdapter != null) { SelectionAdapter.SelectedItem = null; } if (DropDownPopup != null) { DropDownPopup.IsOpen = false; } #if SILVERLIGHT OnDropDownClosed(new RoutedPropertyChangedEventArgs(oldValue, newValue)); #else OnDropDownClosed(new RoutedPropertyChangedEventArgs(oldValue, newValue, DropDownClosedEvent)); #endif } } #endregion /// /// Provides handling for the /// event. /// /// A /// that contains the event data. protected override void OnKeyDown(KeyEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } base.OnKeyDown(e); if (e.Handled || !IsEnabled) { return; } // The drop down is open, pass along the key event arguments to the // selection adapter. If it isn't handled by the adapter's logic, // then we handle some simple navigation scenarios for controlling // the drop down. if (IsDropDownOpen) { if (SelectionAdapter != null) { SelectionAdapter.HandleKeyDown(e); if (e.Handled) { return; } } if (e.Key == Key.Escape) { OnAdapterSelectionCanceled(this, new RoutedEventArgs()); e.Handled = true; } } else { // The drop down is not open, the Down key will toggle it open. if (e.Key == Key.Down) { IsDropDownOpen = true; e.Handled = true; } } // Standard drop down navigation switch (e.Key) { case Key.F4: IsDropDownOpen = !IsDropDownOpen; e.Handled = true; break; case Key.Enter: if (IsDropDownOpen) { OnAdapterSelectionComplete(this, new RoutedEventArgs()); e.Handled = true; } break; default: break; } } /// /// Update the visual state of the control. /// /// /// A value indicating whether to automatically generate transitions to /// the new state, or instantly transition to the new state. /// void IUpdateVisualState.UpdateVisualState(bool useTransitions) { UpdateVisualState(useTransitions); } /// /// Update the current visual state of the button. /// /// /// True to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// internal virtual void UpdateVisualState(bool useTransitions) { // Popup VisualStateManager.GoToState(this, IsDropDownOpen ? VisualStates.StatePopupOpened : VisualStates.StatePopupClosed, useTransitions); // Handle the Common and Focused states Interaction.UpdateVisualStateBase(useTransitions); } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/AutoCompleteFilter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Globalization; using System.Windows.Controls; using System.Windows.Controls.Extensions; namespace System.Windows.Controls { /// /// A predefined set of filter functions for the known, built-in /// AutoCompleteFilterMode enumeration values. /// internal static class AutoCompleteSearch { /// /// Index function that retrieves the filter for the provided /// AutoCompleteFilterMode. /// /// The built-in search mode. /// Returns the string-based comparison function. public static AutoCompleteFilterPredicate GetFilter(AutoCompleteFilterMode FilterMode) { switch (FilterMode) { case AutoCompleteFilterMode.Contains: return Contains; case AutoCompleteFilterMode.ContainsCaseSensitive: return ContainsCaseSensitive; case AutoCompleteFilterMode.ContainsOrdinal: return ContainsOrdinal; case AutoCompleteFilterMode.ContainsOrdinalCaseSensitive: return ContainsOrdinalCaseSensitive; case AutoCompleteFilterMode.Equals: return Equals; case AutoCompleteFilterMode.EqualsCaseSensitive: return EqualsCaseSensitive; case AutoCompleteFilterMode.EqualsOrdinal: return EqualsOrdinal; case AutoCompleteFilterMode.EqualsOrdinalCaseSensitive: return EqualsOrdinalCaseSensitive; case AutoCompleteFilterMode.StartsWith: return StartsWith; case AutoCompleteFilterMode.StartsWithCaseSensitive: return StartsWithCaseSensitive; case AutoCompleteFilterMode.StartsWithOrdinal: return StartsWithOrdinal; case AutoCompleteFilterMode.StartsWithOrdinalCaseSensitive: return StartsWithOrdinalCaseSensitive; case AutoCompleteFilterMode.None: case AutoCompleteFilterMode.Custom: default: return null; } } /// /// Check if the string value begins with the text. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool StartsWith(string text, string value) { return value.StartsWith(text, StringComparison.CurrentCultureIgnoreCase); } /// /// Check if the string value begins with the text. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool StartsWithCaseSensitive(string text, string value) { return value.StartsWith(text, StringComparison.CurrentCulture); } /// /// Check if the string value begins with the text. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool StartsWithOrdinal(string text, string value) { return value.StartsWith(text, StringComparison.OrdinalIgnoreCase); } /// /// Check if the string value begins with the text. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool StartsWithOrdinalCaseSensitive(string text, string value) { return value.StartsWith(text, StringComparison.Ordinal); } /// /// Check if the prefix is contained in the string value. The current /// culture's case insensitive string comparison operator is used. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool Contains(string text, string value) { return value.Contains(text, StringComparison.CurrentCultureIgnoreCase); } /// /// Check if the prefix is contained in the string value. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool ContainsCaseSensitive(string text, string value) { return value.Contains(text, StringComparison.CurrentCulture); } /// /// Check if the prefix is contained in the string value. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool ContainsOrdinal(string text, string value) { return value.Contains(text, StringComparison.OrdinalIgnoreCase); } /// /// Check if the prefix is contained in the string value. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool ContainsOrdinalCaseSensitive(string text, string value) { return value.Contains(text, StringComparison.Ordinal); } /// /// Check if the string values are equal. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool Equals(string text, string value) { return value.Equals(text, StringComparison.CurrentCultureIgnoreCase); } /// /// Check if the string values are equal. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool EqualsCaseSensitive(string text, string value) { return value.Equals(text, StringComparison.CurrentCulture); } /// /// Check if the string values are equal. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool EqualsOrdinal(string text, string value) { return value.Equals(text, StringComparison.OrdinalIgnoreCase); } /// /// Check if the string values are equal. /// /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. public static bool EqualsOrdinalCaseSensitive(string text, string value) { return value.Equals(text, StringComparison.Ordinal); } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/AutoCompleteFilterMode.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; namespace System.Windows.Controls { // When adding to this enum, please update the OnFilterModePropertyChanged // in the AutoCompleteBox class that is used for validation. /// /// Specifies how text in the text box portion of the /// control is used /// to filter items specified by the /// /// property for display in the drop-down. /// /// Stable public enum AutoCompleteFilterMode { /// /// Specifies that no filter is used. All items are returned. /// None = 0, /// /// Specifies a culture-sensitive, case-insensitive filter where the /// returned items start with the specified text. The filter uses the /// /// method, specifying /// as /// the string comparison criteria. /// StartsWith = 1, /// /// Specifies a culture-sensitive, case-sensitive filter where the /// returned items start with the specified text. The filter uses the /// /// method, specifying /// as the string /// comparison criteria. /// StartsWithCaseSensitive = 2, /// /// Specifies an ordinal, case-insensitive filter where the returned /// items start with the specified text. The filter uses the /// /// method, specifying /// as the /// string comparison criteria. /// StartsWithOrdinal = 3, /// /// Specifies an ordinal, case-sensitive filter where the returned items /// start with the specified text. The filter uses the /// /// method, specifying as /// the string comparison criteria. /// StartsWithOrdinalCaseSensitive = 4, /// /// Specifies a culture-sensitive, case-insensitive filter where the /// returned items contain the specified text. /// Contains = 5, /// /// Specifies a culture-sensitive, case-sensitive filter where the /// returned items contain the specified text. /// ContainsCaseSensitive = 6, /// /// Specifies an ordinal, case-insensitive filter where the returned /// items contain the specified text. /// ContainsOrdinal = 7, /// /// Specifies an ordinal, case-sensitive filter where the returned items /// contain the specified text. /// ContainsOrdinalCaseSensitive = 8, /// /// Specifies a culture-sensitive, case-insensitive filter where the /// returned items equal the specified text. The filter uses the /// /// method, specifying /// as /// the search comparison criteria. /// Equals = 9, /// /// Specifies a culture-sensitive, case-sensitive filter where the /// returned items equal the specified text. The filter uses the /// /// method, specifying /// as the string /// comparison criteria. /// EqualsCaseSensitive = 10, /// /// Specifies an ordinal, case-insensitive filter where the returned /// items equal the specified text. The filter uses the /// /// method, specifying /// as the /// string comparison criteria. /// EqualsOrdinal = 11, /// /// Specifies an ordinal, case-sensitive filter where the returned items /// equal the specified text. The filter uses the /// /// method, specifying as /// the string comparison criteria. /// EqualsOrdinalCaseSensitive = 12, /// /// Specifies that a custom filter is used. This mode is used when the /// /// or /// /// properties are set. /// Custom = 13, } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/AutoCompleteFilterPredicate.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// Represents the filter used by the /// control to /// determine whether an item is a possible match for the specified text. /// /// true to indicate is a possible match /// for ; otherwise false. /// The string used as the basis for filtering. /// The item that is compared with the /// parameter. /// The type used for filtering the /// . This type can /// be either a string or an object. /// Stable public delegate bool AutoCompleteFilterPredicate(string search, T item); } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/ISelectionAdapter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Input; namespace System.Windows.Controls { /// /// Defines an item collection, selection members, and key handling for the /// selection adapter contained in the drop-down portion of an /// control. /// /// Stable public interface ISelectionAdapter { /// /// Gets or sets the selected item. /// /// The currently selected item. object SelectedItem { get; set; } /// /// Occurs when the /// /// property value changes. /// event SelectionChangedEventHandler SelectionChanged; /// /// Gets or sets a collection that is used to generate content for the /// selection adapter. /// /// The collection that is used to generate content for the /// selection adapter. IEnumerable ItemsSource { get; set; } /// /// Occurs when a selected item is not cancelled and is committed as the /// selected item. /// event RoutedEventHandler Commit; /// /// Occurs when a selection has been canceled. /// event RoutedEventHandler Cancel; /// /// Provides handling for the /// event that occurs /// when a key is pressed while the drop-down portion of the /// has focus. /// /// A /// that contains data about the /// event. void HandleKeyDown(KeyEventArgs e); /// /// Returns an automation peer for the selection adapter, for use by the /// Silverlight automation infrastructure. /// /// An automation peer for the selection adapter, if one is /// available; otherwise, null. AutomationPeer CreateAutomationPeer(); } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/ItemsControlExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; using System.Windows.Threading; namespace Extensions { //Added from http://stackoverflow.com/questions/2946954/make-listview-scrollintoview-scroll-the-item-into-the-center-of-the-listview-c public static class ItemsControlExtensions { public static void ScrollToCenterOfView(this ItemsControl itemsControl, object item) { // Scroll immediately if possible if (!itemsControl.TryScrollToCenterOfView(item)) { // Otherwise wait until everything is loaded, then scroll if (itemsControl is ListBox) ((ListBox)itemsControl).ScrollIntoView(item); itemsControl.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { itemsControl.TryScrollToCenterOfView(item); })); } } private static bool TryScrollToCenterOfView(this ItemsControl itemsControl, object item) { // Find the container var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as UIElement; if (container == null) return false; // Find the ScrollContentPresenter ScrollContentPresenter presenter = null; for (Visual vis = container; vis != null && vis != itemsControl; vis = VisualTreeHelper.GetParent(vis) as Visual) if ((presenter = vis as ScrollContentPresenter) != null) break; if (presenter == null) return false; // Find the IScrollInfo var scrollInfo = !presenter.CanContentScroll ? presenter : presenter.Content as IScrollInfo ?? FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ?? presenter; // Compute the center point of the container relative to the scrollInfo Size size = container.RenderSize; Point center = container.TransformToAncestor((Visual)scrollInfo).Transform(new Point(size.Width / 2, size.Height / 2)); center.Y += scrollInfo.VerticalOffset; center.X += scrollInfo.HorizontalOffset; // Adjust for logical scrolling if (scrollInfo is StackPanel || scrollInfo is VirtualizingStackPanel) { double logicalCenter = itemsControl.ItemContainerGenerator.IndexFromContainer(container) + 0.5; Orientation orientation = scrollInfo is StackPanel ? ((StackPanel)scrollInfo).Orientation : ((VirtualizingStackPanel)scrollInfo).Orientation; if (orientation == Orientation.Horizontal) center.X = logicalCenter; else center.Y = logicalCenter; } // Scroll the center of the container to the center of the viewport if (scrollInfo.CanVerticallyScroll) scrollInfo.SetVerticalOffset(CenteringOffset(center.Y, scrollInfo.ViewportHeight, scrollInfo.ExtentHeight)); if (scrollInfo.CanHorizontallyScroll) scrollInfo.SetHorizontalOffset(CenteringOffset(center.X, scrollInfo.ViewportWidth, scrollInfo.ExtentWidth)); return true; } private static double CenteringOffset(double center, double viewport, double extent) { return Math.Min(extent - viewport, Math.Max(0, center - viewport / 2)); } private static DependencyObject FirstVisualChild(Visual visual) { if (visual == null) return null; if (VisualTreeHelper.GetChildrenCount(visual) == 0) return null; return VisualTreeHelper.GetChild(visual, 0); } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/PopulatedEventArgs.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Collections; using System.Windows; namespace System.Windows.Controls { /// /// Provides data for the /// /// event. /// /// Stable public class PopulatedEventArgs : RoutedEventArgs { /// /// Gets the list of possible matches added to the drop-down portion of /// the /// control. /// /// The list of possible matches added to the /// . public IEnumerable Data { get; private set; } /// /// Initializes a new instance of the /// . /// /// The list of possible matches added to the /// drop-down portion of the /// control. public PopulatedEventArgs(IEnumerable data) { Data = data; } #if !SILVERLIGHT /// /// Initializes a new instance of the /// . /// /// The list of possible matches added to the /// drop-down portion of the /// control. /// The routed event identifier for this instance. public PopulatedEventArgs(IEnumerable data, RoutedEvent routedEvent) : base(routedEvent) { Data = data; } #endif } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/PopulatedEventHandler.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Diagnostics.CodeAnalysis; using System.Windows; namespace System.Windows.Controls { /// /// Represents the method that will handle the /// /// event of a /// control. /// /// The source of the event. /// A /// that /// contains the event data. /// Stable [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Justification = "There is no generic RoutedEventHandler.")] public delegate void PopulatedEventHandler(object sender, PopulatedEventArgs e); } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/PopulatingEventArgs.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Windows; namespace System.Windows.Controls { /// /// Provides data for the /// /// event. /// /// Stable public class PopulatingEventArgs : RoutedEventArgs { /// /// Gets the text that is used to determine which items to display in /// the /// control. /// /// The text that is used to determine which items to display in /// the . public string Parameter { get; private set; } /// /// Gets or sets a value indicating whether the /// /// event should be canceled. /// /// True to cancel the event, otherwise false. The default is /// false. public bool Cancel { get; set; } /// /// Initializes a new instance of the /// . /// /// The value of the /// /// property, which is used to filter items for the /// control. public PopulatingEventArgs(string parameter) { Parameter = parameter; } #if !SILVERLIGHT /// /// Initializes a new instance of the /// . /// /// The value of the /// /// property, which is used to filter items for the /// control. /// The routed event identifier for this instance. public PopulatingEventArgs(string parameter, RoutedEvent routedEvent) : base(routedEvent) { Parameter = parameter; } #endif } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/PopulatingEventHandler.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Diagnostics.CodeAnalysis; using System.Windows; namespace System.Windows.Controls { /// /// Represents the method that will handle the /// /// event of a /// control. /// /// The source of the event. /// A /// that /// contains the event data. /// Stable [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Justification = "There is no generic RoutedEventHandler.")] public delegate void PopulatingEventHandler(object sender, PopulatingEventArgs e); } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/PopupHelper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; namespace System.Windows.Controls { /// /// PopupHelper is a simple wrapper type that helps abstract platform /// differences out of the Popup. /// internal class PopupHelper { /// /// Gets a value indicating whether a visual popup state is being used /// in the current template for the Closed state. Setting this value to /// true will delay the actual setting of Popup.IsOpen to false until /// after the visual state's transition for Closed is complete. /// public bool UsesClosingVisualState { get; private set; } /// /// Gets or sets the parent control. /// private Control Parent { get; set; } /// /// Gets or sets the maximum drop down height value. /// public double MaxDropDownHeight { get; set; } /// /// Gets the Popup control instance. /// public Popup Popup { get; private set; } /// /// Gets or sets a value indicating whether the actual Popup is open. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Provided for completeness.")] public bool IsOpen { get { return Popup.IsOpen; } set { Popup.IsOpen = value; } } /// /// Gets or sets the popup child framework element. Can be used if an /// assumption is made on the child type. /// private FrameworkElement PopupChild { get; set; } /// /// The Closed event is fired after the Popup closes. /// public event EventHandler Closed; /// /// Fired when the popup children have a focus event change, allows the /// parent control to update visual states or react to the focus state. /// public event EventHandler FocusChanged; /// /// Fired when the popup children intercept an event that may indicate /// the need for a visual state update by the parent control. /// public event EventHandler UpdateVisualStates; /// /// Initializes a new instance of the PopupHelper class. /// /// The parent control. public PopupHelper(Control parent) { Debug.Assert(parent != null, "Parent should not be null."); Parent = parent; } /// /// Initializes a new instance of the PopupHelper class. /// /// The parent control. /// The Popup template part. public PopupHelper(Control parent, Popup popup) : this(parent) { Popup = popup; } /// /// Arrange the popup. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This try-catch pattern is used by other popup controls to keep the runtime up.")] public void Arrange() { if (Popup == null || PopupChild == null || Application.Current == null) { return; } UIElement u = Parent; if (Application.Current.CheckAccess() && Application.Current.Windows.Count > 0) { // TODO: USE THE CURRENT WINDOW INSTEAD! WALK THE TREE! u = Application.Current.Windows[0]; } while ((u as Window) == null && u != null) { u = VisualTreeHelper.GetParent(u) as UIElement; } Window w = u as Window; if (w == null) { return; } double rootWidth = w.ActualWidth; double rootHeight = w.ActualHeight; double popupContentWidth = PopupChild.ActualWidth; double popupContentHeight = PopupChild.ActualHeight; if (rootHeight == 0 || rootWidth == 0 || popupContentWidth == 0 || popupContentHeight == 0) { return; } double rootOffsetX = 0; double rootOffsetY = 0; double myControlHeight = Parent.ActualHeight; double myControlWidth = Parent.ActualWidth; // Use or come up with a maximum popup height. double popupMaxHeight = MaxDropDownHeight; if (double.IsInfinity(popupMaxHeight) || double.IsNaN(popupMaxHeight)) { popupMaxHeight = (rootHeight - myControlHeight) * 3 / 5; } popupContentWidth = Math.Min(popupContentWidth, rootWidth); popupContentHeight = Math.Min(popupContentHeight, popupMaxHeight); popupContentWidth = Math.Max(myControlWidth, popupContentWidth); // We prefer to align the popup box with the left edge of the // control, if it will fit. double popupX = rootOffsetX; if (rootWidth < popupX + popupContentWidth) { // Since it doesn't fit when strictly left aligned, we shift it // to the left until it does fit. popupX = rootWidth - popupContentWidth; popupX = Math.Max(0, popupX); } // We prefer to put the popup below the combobox if it will fit. bool below = true; double popupY = rootOffsetY + myControlHeight; if (rootHeight < popupY + popupContentHeight) { below = false; // It doesn't fit below the combobox, lets try putting it above // the combobox. popupY = rootOffsetY - popupContentHeight; if (popupY < 0) { // doesn't really fit below either. Now we just pick top // or bottom based on wich area is bigger. if (rootOffsetY < (rootHeight - myControlHeight) / 2) { below = true; popupY = rootOffsetY + myControlHeight; } else { below = false; popupY = rootOffsetY - popupContentHeight; } } } // Now that we have positioned the popup we may need to truncate // its size. popupMaxHeight = below ? Math.Min(rootHeight - popupY, popupMaxHeight) : Math.Min(rootOffsetY, popupMaxHeight); Popup.HorizontalOffset = 0; Popup.VerticalOffset = 0; PopupChild.MinWidth = myControlWidth; PopupChild.MaxWidth = rootWidth; PopupChild.MinHeight = 0; PopupChild.MaxHeight = Math.Max(0, popupMaxHeight); PopupChild.Width = popupContentWidth; // PopupChild.Height = popupContentHeight; PopupChild.HorizontalAlignment = HorizontalAlignment.Left; PopupChild.VerticalAlignment = VerticalAlignment.Top; // Set the top left corner for the actual drop down. Canvas.SetLeft(PopupChild, popupX - rootOffsetX); Canvas.SetTop(PopupChild, popupY - rootOffsetY); } /// /// Fires the Closed event. /// /// The event data. private void OnClosed(EventArgs e) { EventHandler handler = Closed; if (handler != null) { handler(this, e); } } /// /// Actually closes the popup after the VSM state animation completes. /// /// Event source. /// Event arguments. private void OnPopupClosedStateChanged(object sender, VisualStateChangedEventArgs e) { // Delayed closing of the popup until now if (e != null && e.NewState != null && e.NewState.Name == VisualStates.StatePopupClosed) { if (Popup != null) { Popup.IsOpen = false; } OnClosed(EventArgs.Empty); } } /// /// Should be called by the parent control before the base /// OnApplyTemplate method is called. /// public void BeforeOnApplyTemplate() { if (UsesClosingVisualState) { // Unhook the event handler for the popup closed visual state group. // This code is used to enable visual state transitions before // actually setting the underlying Popup.IsOpen property to false. VisualStateGroup groupPopupClosed = VisualStates.TryGetVisualStateGroup(Parent, VisualStates.GroupPopup); if (null != groupPopupClosed) { groupPopupClosed.CurrentStateChanged -= OnPopupClosedStateChanged; UsesClosingVisualState = false; } } if (Popup != null) { Popup.Closed -= Popup_Closed; } } /// /// Should be called by the parent control after the base /// OnApplyTemplate method is called. /// public void AfterOnApplyTemplate() { if (Popup != null) { Popup.Closed += Popup_Closed; } VisualStateGroup groupPopupClosed = VisualStates.TryGetVisualStateGroup(Parent, VisualStates.GroupPopup); if (null != groupPopupClosed) { groupPopupClosed.CurrentStateChanged += OnPopupClosedStateChanged; UsesClosingVisualState = true; } // TODO: Consider moving to the DropDownPopup setter // TODO: Although in line with other implementations, what happens // when the template is swapped out? if (Popup != null) { PopupChild = Popup.Child as FrameworkElement; if (PopupChild != null) { #if SILVERLIGHT // For Silverlight only, we just create the popup child with // canvas a single time. if (!_hasControlLoaded) { _hasControlLoaded = true; // Replace the poup child with a canvas PopupChildCanvas = new Canvas(); Popup.Child = PopupChildCanvas; OutsidePopupCanvas = new Canvas(); OutsidePopupCanvas.Background = new SolidColorBrush(Colors.Transparent); OutsidePopupCanvas.MouseLeftButtonDown += OutsidePopup_MouseLeftButtonDown; PopupChildCanvas.Children.Add(OutsidePopupCanvas); PopupChildCanvas.Children.Add(PopupChild); } #endif PopupChild.GotFocus += PopupChild_GotFocus; PopupChild.LostFocus += PopupChild_LostFocus; PopupChild.MouseEnter += PopupChild_MouseEnter; PopupChild.MouseLeave += PopupChild_MouseLeave; PopupChild.SizeChanged += PopupChild_SizeChanged; } } } /// /// The size of the popup child has changed. /// /// The source object. /// The event data. private void PopupChild_SizeChanged(object sender, SizeChangedEventArgs e) { Arrange(); } /// /// The mouse has clicked outside of the popup. /// /// The source object. /// The event data. private void OutsidePopup_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (Popup != null) { Popup.IsOpen = false; } } /// /// Connected to the Popup Closed event and fires the Closed event. /// /// The source object. /// The event data. private void Popup_Closed(object sender, EventArgs e) { OnClosed(EventArgs.Empty); } /// /// Connected to several events that indicate that the FocusChanged /// event should bubble up to the parent control. /// /// The event data. private void OnFocusChanged(EventArgs e) { EventHandler handler = FocusChanged; if (handler != null) { handler(this, e); } } /// /// Fires the UpdateVisualStates event. /// /// The event data. private void OnUpdateVisualStates(EventArgs e) { EventHandler handler = UpdateVisualStates; if (handler != null) { handler(this, e); } } /// /// The popup child has received focus. /// /// The source object. /// The event data. private void PopupChild_GotFocus(object sender, RoutedEventArgs e) { OnFocusChanged(EventArgs.Empty); } /// /// The popup child has lost focus. /// /// The source object. /// The event data. private void PopupChild_LostFocus(object sender, RoutedEventArgs e) { OnFocusChanged(EventArgs.Empty); } /// /// The popup child has had the mouse enter its bounds. /// /// The source object. /// The event data. private void PopupChild_MouseEnter(object sender, MouseEventArgs e) { OnUpdateVisualStates(EventArgs.Empty); } /// /// The mouse has left the popup child's bounds. /// /// The source object. /// The event data. private void PopupChild_MouseLeave(object sender, MouseEventArgs e) { OnUpdateVisualStates(EventArgs.Empty); } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/RoutedPropertyChangingEventArgs.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Windows; namespace System.Windows.Controls { /// /// Provides event data for various routed events that track property values /// changing. Typically the events denote a cancellable action. /// /// /// The type of the value for the dependency property that is changing. /// /// Preview public class RoutedPropertyChangingEventArgs : RoutedEventArgs { /// /// Gets the /// identifier for the property that is changing. /// /// /// The identifier /// for the property that is changing. /// public DependencyProperty Property { get; private set; } /// /// Gets a value that reports the previous value of the changing /// property. /// /// /// The previous value of the changing property. /// public T OldValue { get; private set; } /// /// Gets or sets a value that reports the new value of the changing /// property, assuming that the property change is not cancelled. /// /// /// The new value of the changing property. /// public T NewValue { get; set; } /// /// Gets a value indicating whether the property change that originated /// the RoutedPropertyChanging event is cancellable. /// /// /// True if the property change is cancellable. false if the property /// change is not cancellable. /// public bool IsCancelable { get; private set; } /// /// Gets or sets a value indicating whether the property change that /// originated the RoutedPropertyChanging event should be cancelled. /// /// /// True to cancel the property change; this resets the property to /// . /// false to not cancel the property change; the value changes to /// . /// /// /// Attempted to cancel in an instance where /// /// is false. /// public bool Cancel { get { return _cancel; } set { if (IsCancelable) { _cancel = value; } else if (value) { throw new InvalidOperationException(Properties.Resources.RoutedPropertyChangingEventArgs_CancelSet_InvalidOperation); } } } /// /// Private member variable for Cancel property. /// private bool _cancel; /// /// Gets or sets a value indicating whether internal value coercion is /// acting on the property change that originated the /// RoutedPropertyChanging event. /// /// /// True if coercion is active. false if coercion is not active. /// /// /// This is a total hack to work around the class hierarchy for Value /// coercion in NumericUpDown. /// public bool InCoercion { get; set; } /// /// Initializes a new instance of the /// /// class. /// /// /// The identifier /// for the property that is changing. /// /// The previous value of the property. /// /// The new value of the property, assuming that the property change is /// not cancelled. /// /// /// True if the property change is cancellable by setting /// /// to true in event handling. false if the property change is not /// cancellable. /// public RoutedPropertyChangingEventArgs( DependencyProperty property, T oldValue, T newValue, bool isCancelable) { Property = property; OldValue = oldValue; NewValue = newValue; IsCancelable = isCancelable; Cancel = false; } /// /// Initializes a new instance of the /// /// class. /// /// /// The identifier /// for the property that is changing. /// /// The previous value of the property. /// /// The new value of the property, assuming that the property change is /// not cancelled. /// /// /// True if the property change is cancellable by setting /// /// to true in event handling. false if the property change is not /// cancellable. /// /// The routed event identifier for this instance. public RoutedPropertyChangingEventArgs(DependencyProperty property, T oldValue, T newValue, bool isCancelable, RoutedEvent routedEvent) : base(routedEvent) { Property = property; OldValue = oldValue; NewValue = newValue; IsCancelable = isCancelable; Cancel = false; } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/RoutedPropertyChangingEventHandler.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Windows; namespace System.Windows.Controls { /// /// Represents methods that handle various routed events that track property /// values changing. Typically the events denote a cancellable action. /// /// /// The type of the value for the dependency property that is changing. /// /// /// The object where the initiating property is changing. /// /// Event data for the event. /// Preview [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Justification = "To match pattern of RoutedPropertyChangedEventHandler")] public delegate void RoutedPropertyChangingEventHandler(object sender, RoutedPropertyChangingEventArgs e); } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/SelectorSelectionAdapter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections; using System.Linq; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using Extensions; namespace System.Windows.Controls { /// /// Represents the selection adapter contained in the drop-down portion of /// an control. /// /// Stable public partial class SelectorSelectionAdapter : ISelectionAdapter { /// /// The Selector instance. /// private Selector _selector; /// /// Gets or sets a value indicating whether the selection change event /// should not be fired. /// private bool IgnoringSelectionChanged { get; set; } /// /// Gets or sets the underlying /// /// control. /// /// The underlying /// /// control. public Selector SelectorControl { get { return _selector; } set { if (_selector != null) { _selector.SelectionChanged -= OnSelectionChanged; _selector.MouseLeftButtonUp -= OnSelectorMouseLeftButtonUp; } _selector = value; if (_selector != null) { _selector.SelectionChanged += OnSelectionChanged; _selector.MouseLeftButtonUp += OnSelectorMouseLeftButtonUp; } } } /// /// Occurs when the /// /// property value changes. /// public event SelectionChangedEventHandler SelectionChanged; /// /// Occurs when an item is selected and is committed to the underlying /// /// control. /// public event RoutedEventHandler Commit; /// /// Occurs when a selection is canceled before it is committed. /// public event RoutedEventHandler Cancel; /// /// Initializes a new instance of the /// /// class. /// public SelectorSelectionAdapter() { } /// /// Initializes a new instance of the /// /// class with the specified /// /// control. /// /// The /// control /// to wrap as a /// . public SelectorSelectionAdapter(Selector selector) { SelectorControl = selector; } /// /// Gets or sets the selected item of the selection adapter. /// /// The selected item of the underlying selection adapter. public object SelectedItem { get { return SelectorControl == null ? null : SelectorControl.SelectedItem; } set { IgnoringSelectionChanged = true; if (SelectorControl != null) { SelectorControl.SelectedItem = value; } // Attempt to reset the scroll viewer's position if (value == null) { ResetScrollViewer(); } IgnoringSelectionChanged = false; } } /// /// Gets or sets a collection that is used to generate the content of /// the selection adapter. /// /// The collection used to generate content for the selection /// adapter. public IEnumerable ItemsSource { get { return SelectorControl == null ? null : SelectorControl.ItemsSource; } set { if (SelectorControl != null) { SelectorControl.ItemsSource = value; } } } /// /// If the control contains a ScrollViewer, this will reset the viewer /// to be scrolled to the top. /// private void ResetScrollViewer() { if (SelectorControl != null) { ScrollViewer sv = SelectorControl.GetLogicalChildrenBreadthFirst().OfType().FirstOrDefault(); if (sv != null) { sv.ScrollToTop(); } } } /// /// Handles the mouse left button up event on the selector control. /// /// The source object. /// The event data. private void OnSelectorMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { OnCommit(); } /// /// Handles the SelectionChanged event on the Selector control. /// /// The source object. /// The selection changed event data. private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (IgnoringSelectionChanged) { return; } SelectionChangedEventHandler handler = SelectionChanged; if (handler != null) { handler(sender, e); SelectorControl.ScrollToCenterOfView(SelectorControl.SelectedItem); } } /// /// Increments the /// /// property of the underlying /// /// control. /// protected void SelectedIndexIncrement() { if (SelectorControl != null) { SelectorControl.SelectedIndex = SelectorControl.SelectedIndex + 1 >= SelectorControl.Items.Count ? -1 : SelectorControl.SelectedIndex + 1; } } /// /// Decrements the /// /// property of the underlying /// /// control. /// protected void SelectedIndexDecrement() { if (SelectorControl != null) { int index = SelectorControl.SelectedIndex; if (index >= 0) { SelectorControl.SelectedIndex--; } else if (index == -1) { SelectorControl.SelectedIndex = SelectorControl.Items.Count - 1; } } } /// /// Provides handling for the /// event that occurs /// when a key is pressed while the drop-down portion of the /// has focus. /// /// A /// that contains data about the /// event. public void HandleKeyDown(KeyEventArgs e) { switch (e.Key) { case Key.Enter: OnCommit(); e.Handled = true; break; case Key.Up: SelectedIndexDecrement(); e.Handled = true; break; case Key.Down: if ((ModifierKeys.Alt & Keyboard.Modifiers) == ModifierKeys.None) { SelectedIndexIncrement(); e.Handled = true; } break; case Key.Escape: OnCancel(); e.Handled = true; break; default: break; } } /// /// Raises the /// /// event. /// protected virtual void OnCommit() { OnCommit(this, new RoutedEventArgs()); } /// /// Fires the Commit event. /// /// The source object. /// The event data. private void OnCommit(object sender, RoutedEventArgs e) { RoutedEventHandler handler = Commit; if (handler != null) { handler(sender, e); } AfterAdapterAction(); } /// /// Raises the /// /// event. /// protected virtual void OnCancel() { OnCancel(this, new RoutedEventArgs()); } /// /// Fires the Cancel event. /// /// The source object. /// The event data. private void OnCancel(object sender, RoutedEventArgs e) { RoutedEventHandler handler = Cancel; if (handler != null) { handler(sender, e); } AfterAdapterAction(); } /// /// Change the selection after the actions are complete. /// private void AfterAdapterAction() { IgnoringSelectionChanged = true; if (SelectorControl != null) { SelectorControl.SelectedItem = null; SelectorControl.SelectedIndex = -1; } IgnoringSelectionChanged = false; } /// /// Returns an automation peer for the underlying /// /// control, for use by the Silverlight automation infrastructure. /// /// An automation peer for use by the Silverlight automation /// infrastructure. public AutomationPeer CreateAutomationPeer() { return _selector != null ? FrameworkElementAutomationPeer.CreatePeerForElement(_selector) : null; } } } ================================================ FILE: WpfToolkit/Input/AutoCompleteBox/System/Windows/Controls/ValueByStringHelper.cs ================================================ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Windows.Data; namespace System.Windows.Controls { public class ValueByStringHelper { public static string GetStringValue(string path, Binding binding, object @object) { // see : https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/propertypath-xaml-syntax if (@object == null) return null; var _sb = new StringBuilder(20); var retVal = @object; PropertyInfo indexerP = null; ParameterInfo[] indexers = null; object[] indexerValues = null; int currentIndexer = 0; for (int n = 0; n < path.Length; n++) { var ch = path[n]; if (ch == '.') { if (_sb.Length > 0) { var prp = retVal.GetType().GetProperty(_sb.ToString()); if (prp == null) return null; retVal = prp.GetValue(retVal, null); _sb.Clear(); } } else if (ch == '[') { var prp = retVal.GetType().GetProperty(_sb.ToString()); if (prp == null) return null; retVal = prp.GetValue(retVal, null); _sb.Clear(); indexerP = retVal.GetType().GetProperties().FirstOrDefault(x => x.GetIndexParameters().Any()); if (indexerP == null) return null; indexers = indexerP.GetIndexParameters(); indexerValues = new object[indexers.Length]; } else if (ch == ',') { indexerValues[currentIndexer] = Convert.ChangeType(_sb.ToString(), indexers[currentIndexer].ParameterType); currentIndexer++; _sb.Clear(); } else if (ch == ']') { indexerValues[currentIndexer] = Convert.ChangeType(_sb.ToString(), indexers[currentIndexer].ParameterType); _sb.Clear(); retVal = indexerP.GetValue(retVal, indexerValues); if (retVal == null) return null; currentIndexer = 0; indexers = null; indexerValues = null; } else { _sb.Append(ch); } } if (_sb.Length > 0) { var prp = retVal.GetType().GetProperty(_sb.ToString()); if (prp == null) return null; retVal = prp.GetValue(retVal, null); } if (binding != null) { if (binding.Converter != null) { retVal = binding.Converter.Convert(retVal, null, binding.ConverterParameter, binding.ConverterCulture); } var formattableRetVal = retVal as IFormattable; if (formattableRetVal != null) { return formattableRetVal.ToString(binding.StringFormat, binding.ConverterCulture); } var convertibleRetVal = retVal as IConvertible; if (convertibleRetVal != null) { return convertibleRetVal.ToString(binding.ConverterCulture); } } return retVal != null ? retVal.ToString() : null; } } } ================================================ FILE: WpfToolkit/Input/DotNetProjects.Input.Toolkit.csproj ================================================  net5.0-windows;net6.0-windows;net4 true System.Windows.Controls WPF Toolkit Input true DotNetProjects.snk true DotNetProjects.WpfToolkit.Input DotNetProjects DotNetProjects DotNetProjects.WPF Toolkit 2021 DotNetProjects 1.0.0 1.0.0.0 1.0.0.0 MS-PL Common\Extensions.cs Common\InteractionHelper.cs Common\ItemsControlHelper.cs Common\IUpdateVisualState.cs Common\VisualStates.cs Common\VisualTreeExtensions.cs Common\WeakEventListener.cs ================================================ FILE: WpfToolkit/Input/DotNetProjects.Input.Toolkit.csproj.DotSettings ================================================  True True True ================================================ FILE: WpfToolkit/Input/GlobalSuppressions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to [###LICENSE_NAME###]. // Please see [###LICENSE_LINK###] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Automation.Peers", Justification = "Official automation peers namespace.")] // UpDownBase does not exist on WPF [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.UpDownBase", Justification = "UpDownBase does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.UpDownBase`1", Justification = "UpDownBase does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.UpDownParseErrorEventArgs", Justification = "UpDownBase does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Automation.Peers.UpDownBaseAutomationPeer`1", Justification = "UpDownBase does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.UpDownParsingEventArgs`1")] // Spinner does not exist on WPF [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.Spinner", Justification = "NumericUpDown does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.SpinDirection", Justification = "NumericUpDown does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.SpinEventArgs", Justification = "NumericUpDown does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.ButtonSpinner", Justification = "NumericUpDown does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.ValidSpinDirections", Justification = "ValidSpinDirections does not exist on WPF.")] // NumericUpDown does not exist on WPF [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Automation.Peers.NumericUpDownAutomationPeer", Justification = "Spinner does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.NumericUpDown", Justification = "Spinner does not exist on WPF.")] // time input controls do not exist in wpf [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.Picker")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.PopupButtonMode")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.TimeGlobalizationInfo")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.TimeParser")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.TimeParserCollection")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.TimePicker")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.TimePickerFormat")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.TimeUpDown")] // DomainUpDown does not exist on WPF [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Automation.Peers.DomainUpDownAutomationPeer", Justification = "DomainUpDown does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DomainUpDown", Justification = "DomainUpDown does not exist on WPF.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.InvalidInputAction", Justification = "DomainUpDown does not exist on WPF.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowKeyDown(System.Windows.Input.KeyEventArgs)", Justification = "Complete implementation used in other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowKeyUp(System.Windows.Input.KeyEventArgs)", Justification = "Complete implementation used in other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs)", Justification = "Complete implementation used in other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#ClickCount", Justification = "Complete implementation used in other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#LastClickPosition", Justification = "Complete implementation used in other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#LastClickTime", Justification = "Complete implementation used in other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnMouseLeftButtonDownBase()", Justification = "Complete implementation used in other assemblies.")] // TypeConverter linked in [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.TypeConverters.#CanConvertFrom`1(System.Type)", Justification = "Linked in file.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.TypeConverters.#ConvertFrom`1(System.ComponentModel.TypeConverter,System.Object)", Justification = "Linked in file.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Controls.Primitives", Justification = "Expecting more types.")] [assembly: SuppressMessage("Layout", "SWC3000:CanvasDoesNotRespectLayout", MessageId = "ControlTemplate", Justification = "Canvas is used because of it does not interfere with layout considerations.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "hh", Justification = "Indicates hours in a DateTimeFormat.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "ss", Justification = "Indicates seconds in a DateTimeFormat.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Headered")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Silverlight")] ================================================ FILE: WpfToolkit/Input/Properties/AssemblyInfo.cs ================================================ using System.Windows; // In order to begin building localizable applications, set // CultureYouAreCodingWith in your .csproj file // inside a . For example, if you are using US english // in your source files, set the to en-US. Then uncomment // the NeutralResourceLanguage attribute below. Update the "en-US" in // the line below to match the UICulture setting in the project file. //[assembly: NeutralResourcesLanguage("en-US")] // WPF-only settings [assembly: ThemeInfo( ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] ================================================ FILE: WpfToolkit/Input/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.18213 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Windows.Controls.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Windows.Controls.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Invalid FilterMode enumeration value. The value must be one of the defined AutoCompleteFilterMode values to be accepted.. /// internal static string AutoComplete_OnFilterModePropertyChanged_InvalidValue { get { return ResourceManager.GetString("AutoComplete_OnFilterModePropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid maximum drop down height value '{0}'. The value must be greater than or equal to zero.. /// internal static string AutoComplete_OnMaxDropDownHeightPropertyChanged_InvalidValue { get { return ResourceManager.GetString("AutoComplete_OnMaxDropDownHeightPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid MinimumPopulateDelay value '{0}'. The value must be greater than or equal to zero.. /// internal static string AutoComplete_OnMinimumPopulateDelayPropertyChanged_InvalidValue { get { return ResourceManager.GetString("AutoComplete_OnMinimumPopulateDelayPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set read-only property SearchText.. /// internal static string AutoComplete_OnSearchTextPropertyChanged_InvalidWrite { get { return ResourceManager.GetString("AutoComplete_OnSearchTextPropertyChanged_InvalidWrite", resourceCulture); } } /// /// Looks up a localized string similar to Cannot perform operation.. /// internal static string Automation_OperationCannotBePerformed { get { return ResourceManager.GetString("Automation_OperationCannotBePerformed", resourceCulture); } } /// /// Looks up a localized string similar to Invalid index value '{0}'. /// internal static string DomainUpDown_CurrentIndex_InvalidValue { get { return ResourceManager.GetString("DomainUpDown_CurrentIndex_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid InvalidInputAction value '{0}'. /// internal static string DomainUpDown_InvalidInputAction_InvalidValue { get { return ResourceManager.GetString("DomainUpDown_InvalidInputAction_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid ExpandDirection value '{0}'.. /// internal static string Expander_OnExpandDirectionPropertyChanged_InvalidValue { get { return ResourceManager.GetString("Expander_OnExpandDirectionPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid attempt to change read-only property "{0}.". /// internal static string InvalidAttemptToChangeReadOnlyProperty { get { return ResourceManager.GetString("InvalidAttemptToChangeReadOnlyProperty", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set read-only property TimeItemsSelection.. /// internal static string ListTimePickerPopup_TimeItemsSelection_ReadOnly { get { return ResourceManager.GetString("ListTimePickerPopup_TimeItemsSelection_ReadOnly", resourceCulture); } } /// /// Looks up a localized string similar to Invalid double value '{0}': valid value is of double type and within decimal range.. /// internal static string NumericUpDown_EnsureValidDoubleValue_InvalidDoubleValue { get { return ResourceManager.GetString("NumericUpDown_EnsureValidDoubleValue_InvalidDoubleValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid Increment value '{0}': valid value is of double type, within decimal range, and positive. /// internal static string NumericUpDown_EnsureValidIncrementValue_InvalidValue { get { return ResourceManager.GetString("NumericUpDown_EnsureValidIncrementValue_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid DecimalPlaces value '{0}': valid value is of int type and within 0 and 15 inclusive.. /// internal static string NumericUpDown_OnDecimalPlacesPropertyChanged_InvalidValue { get { return ResourceManager.GetString("NumericUpDown_OnDecimalPlacesPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to The collection is currently ReadOnly and should not be modified.. /// internal static string ObservableObjectCollection_ReadOnly { get { return ResourceManager.GetString("ObservableObjectCollection_ReadOnly", resourceCulture); } } /// /// Looks up a localized string similar to Invalid maximum drop down height value '{0}'. The value must be greater than or equal to zero.. /// internal static string Picker_OnMaxDropDownHeightPropertyChanged_InvalidValue { get { return ResourceManager.GetString("Picker_OnMaxDropDownHeightPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid PopupButtonMode value '{0}'.. /// internal static string Picker_PopupButtonModeNotValid { get { return ResourceManager.GetString("Picker_PopupButtonModeNotValid", resourceCulture); } } /// /// Looks up a localized string similar to Value must be larger than or equal to 0.. /// internal static string Rating_SetItemCount_ItemCountMustBeLargerThanOrEqualToZero { get { return ResourceManager.GetString("Rating_SetItemCount_ItemCountMustBeLargerThanOrEqualToZero", resourceCulture); } } /// /// Looks up a localized string similar to Rating. /// internal static string RatingAutomationPeer_GetLocalizedControlTypeCore { get { return ResourceManager.GetString("RatingAutomationPeer_GetLocalizedControlTypeCore", resourceCulture); } } /// /// Looks up a localized string similar to Value must be null or a number between 0 and 1.. /// internal static string RatingAutomationPeer_SetValue { get { return ResourceManager.GetString("RatingAutomationPeer_SetValue", resourceCulture); } } /// /// Looks up a localized string similar to Star. /// internal static string RatingItemAutomationPeer_GetLocalizedControlTypeCore { get { return ResourceManager.GetString("RatingItemAutomationPeer_GetLocalizedControlTypeCore", resourceCulture); } } /// /// Looks up a localized string similar to The RoutedPropertyChangingEvent cannot be canceled.. /// internal static string RoutedPropertyChangingEventArgs_CancelSet_InvalidOperation { get { return ResourceManager.GetString("RoutedPropertyChangingEventArgs_CancelSet_InvalidOperation", resourceCulture); } } /// /// Looks up a localized string similar to Spin action is not valid at this moment.. /// internal static string Spinner_SpinNotValid { get { return ResourceManager.GetString("Spinner_SpinNotValid", resourceCulture); } } /// /// Looks up a localized string similar to Invalid PopupMinutesInterval '{0}'. The interval can be set to 0 (no interval) to and including 59.. /// internal static string TimeInput_PopupMinutesInterval_InvalidValue { get { return ResourceManager.GetString("TimeInput_PopupMinutesInterval_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid PopupSecondsInterval '{0}'.The interval can be set to 0 (no interval) to and including 59.. /// internal static string TimeInput_PopupSecondsInterval_InvalidValue { get { return ResourceManager.GetString("TimeInput_PopupSecondsInterval_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set the PopupProperty in a style. Please use PopupTemplate.. /// internal static string TimePicker_PopupSetInStyle { get { return ResourceManager.GetString("TimePicker_PopupSetInStyle", resourceCulture); } } /// /// Looks up a localized string similar to Invalid PopupTimeSelectionMode for this popup, value '{0}'.. /// internal static string TimePicker_PopupTimeSelectionModeNotValid { get { return ResourceManager.GetString("TimePicker_PopupTimeSelectionModeNotValid", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set read-only property TimeHintContent.. /// internal static string TimeUpDown_OnTimeHintContentChanged { get { return ResourceManager.GetString("TimeUpDown_OnTimeHintContentChanged", resourceCulture); } } /// /// Looks up a localized string similar to '{0}' is unable to convert '{1}' to '{2}'.. /// internal static string TypeConverters_Convert_CannotConvert { get { return ResourceManager.GetString("TypeConverters_Convert_CannotConvert", resourceCulture); } } /// /// Looks up a localized string similar to '{0}' cannot convert from '{1}'.. /// internal static string TypeConverters_ConvertFrom_CannotConvertFromType { get { return ResourceManager.GetString("TypeConverters_ConvertFrom_CannotConvertFromType", resourceCulture); } } /// /// Looks up a localized string similar to Cannot parse text '{0}'. /// internal static string UpDown_ParseException { get { return ResourceManager.GetString("UpDown_ParseException", resourceCulture); } } } } ================================================ FILE: WpfToolkit/Input/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Cannot perform operation. Exception thrown by automation peers. Invalid index value '{0}' Exception thrown when the index is not within the collection. Invalid InvalidInputAction value '{0}' Exception thrown when the InvalidInputAction property is provided an invalid value. Invalid ExpandDirection value '{0}'. Exception thrown when the ExpandDirection property is provided an invalid value. Cannot set read-only property TimeItemsSelection. Exception thrown when a TimeItemsSelection is being set. Invalid attempt to change read-only property "{0}." Exception thrown when a read-only property is changed. Invalid double value '{0}': valid value is of double type and within decimal range. Exception thrown when dependency properties are provided an value that's not a valid double value or not in the decimal range. Invalid Increment value '{0}': valid value is of double type, within decimal range, and positive Exception thrown when the Increment property is provided an value that's not a double value, or not in decimal range, or is negative or zero. Invalid DecimalPlaces value '{0}': valid value is of int type and within 0 and 15 inclusive. Exception thrown when the DecimalPlaces property is provided an value not in the range of 0 and 15 inclusive. The collection is currently ReadOnly and should not be modified. Exception thrown when the collection is readonly and was modified. Invalid maximum drop down height value '{0}'. The value must be greater than or equal to zero. Exception thrown when the MaxDropDownHeight dependency property is seto to a value that is negative. Invalid PopupButtonMode value '{0}'. Exception thrown when the PopupButtonMode is improperly set. Spin action is not valid at this moment. Exception thrown when spinning in a direction that is not currently valid. Invalid PopupMinutesInterval '{0}'. The interval can be set to 0 (no interval) to and including 59. Exception thrown when an invalid interval is set. Invalid PopupSecondsInterval '{0}'.The interval can be set to 0 (no interval) to and including 59. Exception thrown when an invalid interval is set. Cannot set the PopupProperty in a style. Please use PopupTemplate. Exception thrown when the PopupProperty is set in a style. Invalid PopupTimeSelectionMode for this popup, value '{0}'. Exception thrown by a TimePickerPopup if it does not support the selected PopupTimeSelectionMode. Cannot set read-only property TimeHintContent. Exception thrown when the TimeHintContent property is improperly set. '{0}' cannot convert from '{1}'. Exception thrown when a type converter is asked to convert something it cannot. '{0}' is unable to convert '{1}' to '{2}'. Exception thrown when a type converter fails to convert a value to another type. Cannot parse text '{0}' Exception thrown when the text supplied cannot be parsed. Value must be null or a number between 0 and 1. Exception thrown when a change is made to an automation peer. Rating The localized control type used by the RatingAutomationPeer. Star The localized control type name used by the RatingItemAutomationPeer. Value must be larger than or equal to 0. Exception thrown when the ItemCount is set to a value smaller than 0. Invalid FilterMode enumeration value. The value must be one of the defined AutoCompleteFilterMode values to be accepted. Exception thrown when the FilterMode dependency property is set to a value that does not pass the enum validation step in the property changed handler. Invalid maximum drop down height value '{0}'. The value must be greater than or equal to zero. Exception thrown when the MaxDropDownHeight dependency property is seto to a value that is negative. Invalid MinimumPopulateDelay value '{0}'. The value must be greater than or equal to zero. Exception thrown when the MinimumPopulateDelay dependency property is changed to a negative value. Cannot set read-only property SearchText. Exception thrown when the SearchText property is improperly set. The RoutedPropertyChangingEvent cannot be canceled. Exception thrown when setting Cancel property while IsCancelable is false. ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Automation/Peers/RatingAutomationPeer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Automation.Provider; using System.Windows.Controls; namespace System.Windows.Automation.Peers { /// /// Exposes Rating types to UI Automation. /// /// Preview public sealed partial class RatingAutomationPeer : ItemsControlAutomationPeer, IValueProvider, ISelectionProvider { /// /// Provides initialization for base class values when called by the constructor /// of a derived class. /// /// The item to create the automation peer for. /// The item automation peer. protected override ItemAutomationPeer CreateItemAutomationPeer(object item) { ItemAutomationPeer peer = null; UIElement element = item as UIElement; if (element != null) { peer = FrameworkElementAutomationPeer.CreatePeerForElement(element) as ItemAutomationPeer; } return peer; } /// /// Gets the Rating that owns this RatingAutomationPeer. /// /// The Rating. private Rating OwnerRating { get { return (Rating)Owner; } } /// /// Initializes a new instance of the class. /// /// The Rating that is associated with this /// RatingAutomationPeer. public RatingAutomationPeer(Rating owner) : base(owner) { } /// /// Returns a name if no name is set. /// /// A name if no name is set. protected override string GetNameCore() { string name = base.GetNameCore(); if (string.IsNullOrEmpty(name)) { return "Rating"; } return name; } /// /// Returns the localized control type. /// /// The localized control type. protected override string GetLocalizedControlTypeCore() { return System.Windows.Controls.Properties.Resources.RatingAutomationPeer_GetLocalizedControlTypeCore; } /// /// Gets the control type for the Rating that is associated /// with this RatingAutomationPeer. This method is called by /// GetAutomationControlType. /// /// List AutomationControlType. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Slider; } /// /// Gets the control pattern for the Rating that is associated /// with this RatingAutomationPeer. /// /// The desired PatternInterface. /// The desired AutomationPeer or null. public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.Value || patternInterface == PatternInterface.Selection) { return this; } return base.GetPattern(patternInterface); } /// /// Gets the collection of child elements of /// the that is /// associated with this . /// /// /// A collection of RatingItemAutomationPeer elements, or null if the /// Rating that is associated with this RatingAutomationPeer is /// empty. /// [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "Required by automation")] protected override List GetChildrenCore() { Rating owner = OwnerRating; ItemCollection items = owner.Items; if (items.Count <= 0) { return null; } List peers = new List(items.Count); for (int i = 0; i < items.Count; i++) { RatingItem element = owner.ItemContainerGenerator.ContainerFromIndex(i) as RatingItem; if (element != null) { peers.Add(FromElement(element) ?? CreatePeerForElement(element)); } } return peers; } /// /// Gets a value indicating whether the UI Automation provider /// allows more than one child element to be selected concurrently. /// /// true if multiple selection is allowed; otherwise, false. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// public bool CanSelectMultiple { get { return false; } } /// /// Retrieves a UI Automation provider for each child element that is /// selected. /// /// An array of UI Automation providers. /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// public IRawElementProviderSimple[] GetSelection() { RatingItem selectedRatingItem = OwnerRating.GetRatingItems().LastOrDefault(ratingItem => ratingItem.Value > 0.0); if (selectedRatingItem != null) { return new[] { ProviderFromPeer(FromElement(selectedRatingItem)) }; } return new IRawElementProviderSimple[] { }; } /// /// Gets a value indicating whether the UI Automation provider /// requires at least one child element to be selected. /// /// true if selection is required; otherwise, false. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// public bool IsSelectionRequired { get { return false; } } /// /// Gets a value indicating whether the Rating is read-only. /// public bool IsReadOnly { get { return OwnerRating.IsReadOnly; } } /// /// Sets a rating value. /// /// The value of the rating. public void SetValue(string value) { double ratingValue; if (string.IsNullOrEmpty(value)) { OwnerRating.Value = null; } else if (double.TryParse(value, out ratingValue)) { if (ratingValue < 0.0 || ratingValue > 1.0) { throw new InvalidOperationException(System.Windows.Controls.Properties.Resources.RatingAutomationPeer_SetValue); } else { OwnerRating.Value = ratingValue; } } else { throw new InvalidOperationException(System.Windows.Controls.Properties.Resources.RatingAutomationPeer_SetValue); } } /// /// Gets the rating value. /// public string Value { get { if (OwnerRating.Value.HasValue) { return OwnerRating.Value.ToString(); } else { return null; } } } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Automation/Peers/RatingItemAutomationPeer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Windows.Automation.Provider; using System.Windows.Controls; [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.RatingItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.AddToSelection()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.RatingItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.IsSelected", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.RatingItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.RemoveFromSelection()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.RatingItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.Select()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.RatingItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.SelectionContainer", Justification = "Required for subset compat with WPF")] namespace System.Windows.Automation.Peers { /// /// Exposes RatingItem types to UI Automation. /// /// Preview public class RatingItemAutomationPeer : FrameworkElementAutomationPeer, ISelectionItemProvider { /// /// Gets the RatingItem that owns this RatingItemAutomationPeer. /// private RatingItem OwnerRatingItem { get { return (RatingItem)Owner; } } /// /// Initializes a new instance of the RatingAutomationPeer class. /// /// /// The Rating that is associated with this /// RatingAutomationPeer. /// public RatingItemAutomationPeer(RatingItem owner) : base(owner) { } /// /// Returns the localized control type. /// /// The localized control type. protected override string GetLocalizedControlTypeCore() { return System.Windows.Controls.Properties.Resources.RatingItemAutomationPeer_GetLocalizedControlTypeCore; } /// /// Gets the control type for the RatingItem that is associated /// with this RatingItemAutomationPeer. This method is called by /// GetAutomationControlType. /// /// Custom AutomationControlType. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.ListItem | AutomationControlType.Button; } /// /// Gets the name of the RatingItem that is associated with this /// RatingItemAutomationPeer. This method is called by GetClassName. /// /// The name RatingItem. protected override string GetClassNameCore() { return "RatingItem"; } /// /// Gets the control pattern for the RatingItem that is associated /// with this RatingItemAutomationPeer. /// /// The desired PatternInterface. /// The desired AutomationPeer or null. public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.SelectionItem) { return this; } return null; } /// /// Returns the name of the rating item. Uses the index of the rating /// item in the list. /// /// The name of the rating item. protected override string GetNameCore() { int? index = this.OwnerRatingItem.ParentRating.GetRatingItems().IndexOf(this.OwnerRatingItem); if (index != null) { return (index.Value + 1).ToString(CultureInfo.CurrentUICulture); } else { return string.Empty; } } /// /// Adds the RatingItem to the collection of selected items. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void ISelectionItemProvider.AddToSelection() { RatingItem owner = OwnerRatingItem; Rating parent = owner.ParentRating; if (parent == null || parent.Value != null) { throw new InvalidOperationException(Controls.Properties.Resources.Automation_OperationCannotBePerformed); } owner.SelectValue(); } /// /// Gets a value indicating whether the Rating is selected. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// bool ISelectionItemProvider.IsSelected { get { return OwnerRatingItem.Value > 0.0; } } /// /// Removes the current Rating from the collection of selected /// items. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void ISelectionItemProvider.RemoveFromSelection() { RatingItem owner = OwnerRatingItem; Rating parent = owner.ParentRating; if (parent == null) { throw new InvalidOperationException(Controls.Properties.Resources.Automation_OperationCannotBePerformed); } if (!parent.IsReadOnly) { parent.Value = null; } } /// /// Clears selection from currently selected items and then proceeds to /// select the current Rating. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void ISelectionItemProvider.Select() { OwnerRatingItem.SelectValue(); } /// /// Gets the UI Automation provider that implements ISelectionProvider /// and acts as the container for the calling object. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { Rating parent = OwnerRatingItem.ParentRating; if (parent != null) { AutomationPeer peer = FromElement(parent); if (peer != null) { return ProviderFromPeer(peer); } } return null; } } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/Clipper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls.Primitives { /// /// Clips a ratio of its content. /// /// Preview public abstract class Clipper : ContentControl { #region public double RatioVisible /// /// Gets or sets the percentage of the item visible. /// public double RatioVisible { get { return (double)GetValue(RatioVisibleProperty); } set { SetValue(RatioVisibleProperty, value); } } /// /// Identifies the RatioVisible dependency property. /// public static readonly DependencyProperty RatioVisibleProperty = DependencyProperty.Register( "RatioVisible", typeof(double), typeof(Clipper), new PropertyMetadata(1.0, OnRatioVisibleChanged)); /// /// RatioVisibleProperty property changed handler. /// /// PartialView that changed its RatioVisible. /// Event arguments. private static void OnRatioVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Clipper source = (Clipper)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; source.OnRatioVisibleChanged(oldValue, newValue); } /// /// RatioVisibleProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnRatioVisibleChanged(double oldValue, double newValue) { if (newValue >= 0.0 && newValue <= 1.0) { ClipContent(); } else { if (newValue < 0.0) { this.RatioVisible = 0.0; } else if (newValue > 1.0) { this.RatioVisible = 1.0; } } } #endregion public double RatioVisible /// /// Initializes a new instance of the Clipper class. /// protected Clipper() { this.SizeChanged += delegate { ClipContent(); }; } /// /// Updates the clip geometry. /// protected abstract void ClipContent(); } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/EnumerableFunctions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; namespace System.Windows.Controls { /// /// This class contains general purpose functions to manipulate the generic /// IEnumerable type. /// internal static class EnumerableFunctions { /////// /////// Applies a function to an accumulated value and an item in the /////// sequence and yields the result as the accumulated value. /////// /////// The type of the sequence. /////// The sequence to scan. /////// The function applied to the accumulator and the /////// current item. /////// A sequence of computed values. ////public static IEnumerable Scan(this IEnumerable that, Func func) ////{ //// IEnumerator enumerator = that.GetEnumerator(); //// if (!enumerator.MoveNext()) //// { //// yield break; //// } //// T acc = enumerator.Current; //// yield return acc; //// while (enumerator.MoveNext()) //// { //// acc = func(acc, enumerator.Current); //// yield return acc; //// } ////} /// /// Applies a function to an accumulated value and an item in the /// sequence and yields the result as the accumulated value. /// /// The type of the input sequence. /// The type of the initial value. /// The sequence to scan. /// The function applied to the accumulator and the /// current item. /// The initial value in the output sequence. /// /// A sequence of computed values. public static IEnumerable Scan(this IEnumerable that, Func func, R initialValue) { R acc = initialValue; yield return acc; IEnumerator enumerator = that.GetEnumerator(); while (enumerator.MoveNext()) { acc = func(acc, enumerator.Current); yield return acc; } } /// /// Accepts two sequences and applies a function to the corresponding /// values in the two sequences. /// /// The type of the first sequence. /// The type of the second sequence. /// The return type of the function. /// The first sequence. /// The second sequence. /// The function to apply to the corresponding values /// from the two sequences. /// A sequence of transformed values from both sequences. public static IEnumerable Zip(IEnumerable enumerable0, IEnumerable enumerable1, Func func) { IEnumerator enumerator0 = enumerable0.GetEnumerator(); IEnumerator enumerator1 = enumerable1.GetEnumerator(); while (enumerator0.MoveNext() && enumerator1.MoveNext()) { yield return func(enumerator0.Current, enumerator1.Current); } } /// /// Returns the index of an item in a sequence. /// /// The type of the sequence. /// The sequence. /// The item in the sequence. /// The index of an item in a sequence. public static int? IndexOf(this IEnumerable that, T item) { IEnumerator enumerator = that.GetEnumerator(); int index = 0; while (enumerator.MoveNext()) { if (object.ReferenceEquals(enumerator.Current, item)) { return index; } index++; } return null; } /// /// Returns a stream of weighted values based on a percentage. /// /// A sequence of values. /// The percentage of values. /// A sequence of percentages. public static IEnumerable GetWeightedValues(this IEnumerable values, double percent) { double total = values.Sum(); if (total == 0) { return values.Select(_ => 0.0); } return EnumerableFunctions .Zip( values.Scan((acc, current) => acc + current, 0.0), values, (acc, current) => Tuple.Create(acc, current)) .Select(tuple => Tuple.Create(tuple.First / total, tuple.Second / total)) .Select(tuple => { double accumulated = tuple.First; double current = tuple.Second; if (percent > accumulated && accumulated + current > percent) { return (percent - accumulated) * total; } else if (percent <= accumulated) { return 0.0; } else { return 1.0; } }); } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/LinearClipper.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Windows.Media; namespace System.Windows.Controls.Primitives { /// /// Clips the content of the control in a given direction. /// /// Preview public class LinearClipper : Clipper { #region public ExpandDirection ExpandDirection /// /// Gets or sets the clipped edge. /// public ExpandDirection ExpandDirection { get { return (ExpandDirection) GetValue(ExpandDirectionProperty); } set { SetValue(ExpandDirectionProperty, value); } } /// /// Identifies the ExpandDirection dependency property. /// public static readonly DependencyProperty ExpandDirectionProperty = DependencyProperty.Register( "ExpandDirection", typeof(ExpandDirection), typeof(LinearClipper), new PropertyMetadata(ExpandDirection.Right, OnExpandDirectionChanged)); /// /// ExpandDirectionProperty property changed handler. /// /// ExpandDirectionView that changed its ExpandDirection. /// Event arguments. private static void OnExpandDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LinearClipper source = (LinearClipper)d; ExpandDirection oldValue = (ExpandDirection)e.OldValue; ExpandDirection newValue = (ExpandDirection)e.NewValue; source.OnExpandDirectionChanged(oldValue, newValue); } /// /// ExpandDirectionProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnExpandDirectionChanged(ExpandDirection oldValue, ExpandDirection newValue) { ClipContent(); } #endregion public ExpandDirection ExpandDirection /// /// Updates the clip geometry. /// protected override void ClipContent() { if (ExpandDirection == ExpandDirection.Right) { double width = this.RenderSize.Width * RatioVisible; this.Clip = new RectangleGeometry { Rect = new Rect(0, 0, width, this.RenderSize.Height) }; } else if (ExpandDirection == ExpandDirection.Left) { double width = this.RenderSize.Width * RatioVisible; double rightSide = this.RenderSize.Width - width; this.Clip = new RectangleGeometry { Rect = new Rect(rightSide, 0, width, this.RenderSize.Height) }; } else if (ExpandDirection == ExpandDirection.Up) { double height = this.RenderSize.Height * RatioVisible; double bottom = this.RenderSize.Height - height; this.Clip = new RectangleGeometry { Rect = new Rect(0, bottom, this.RenderSize.Width, height) }; } else if (ExpandDirection == ExpandDirection.Down) { double height = this.RenderSize.Height * RatioVisible; this.Clip = new RectangleGeometry { Rect = new Rect(0, 0, this.RenderSize.Width, height) }; } } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/NullableConverter.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.ComponentModel; using System.Globalization; namespace System.Windows.Controls { /// /// Converts a string or base value to a value. /// /// The type should be value type. /// Preview public class NullableConverter : TypeConverter where T : struct { /// /// Returns whether the type converter can convert an object from the /// specified type to the type of this converter. /// /// An object that provides a format context. /// /// The type you want to convert from. /// /// Returns true if this converter can perform the conversion; /// otherwise, false. /// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(T)) { return true; } else if (sourceType == typeof(string)) { return true; } return false; } /// /// Returns whether the type converter can convert an object from the /// specified type to the type of this converter. /// /// An object that provides a format context. /// /// The type you want to convert to. /// /// /// Returns true if this converter can perform the conversion; /// otherwise, false. /// public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return (destinationType == typeof(T)); } /// /// Converts from the specified value to the type of this converter. /// /// An object that provides a format context. /// /// The /// to use as the /// current culture. /// The value to convert to the type of this /// converter. /// The converted value. /// /// The conversion cannot be performed. /// public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { string stringValue = value as string; if (value is T) { return new Nullable((T)value); } else if (string.IsNullOrEmpty(stringValue) || String.Equals(stringValue, "Auto", StringComparison.OrdinalIgnoreCase)) { return new Nullable(); } return new Nullable((T)Convert.ChangeType(value, typeof(T), culture)); } /// /// Converts from the specified value to the a specified type from the /// type of this converter. /// /// An object that provides a format context. /// /// The /// to use as the /// current culture. /// The value to convert to the type of this /// converter. /// The type of convert the value to /// . /// The converted value. /// /// The conversion cannot be performed. /// public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value == null) { return string.Empty; } else if (destinationType == typeof(string)) { return value.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/Rating.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media.Animation; namespace System.Windows.Controls { /// /// A control that has a rating. /// /// Preview [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateReadOnly, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RatingItem))] public class Rating : ItemsControl, IUpdateVisualState { #region protected double DisplayValue /// /// Gets or sets the actual value of the Rating control. /// protected double DisplayValue { get { return (double)GetValue(DisplayValueProperty); } set { SetValue(DisplayValueProperty, value); } } /// /// Identifies the DisplayValue dependency property. /// protected static readonly DependencyProperty DisplayValueProperty = DependencyProperty.Register( "DisplayValue", typeof(double), typeof(Rating), new PropertyMetadata(0.0, OnDisplayValueChanged)); /// /// DisplayValueProperty property changed handler. /// /// Rating that changed its DisplayValue. /// Event arguments. private static void OnDisplayValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) { Rating source = (Rating)dependencyObject; source.OnDisplayValueChanged(); } /// /// DisplayValueProperty property changed handler. /// private void OnDisplayValueChanged() { UpdateDisplayValues(); } #endregion protected double DisplayValue /// /// Gets or sets the rating item hovered over. /// private RatingItem HoveredRatingItem { get; set; } /// /// Gets the helper that provides all of the standard /// interaction functionality. /// internal InteractionHelper Interaction { get; private set; } #region public int ItemCount /// /// Gets or sets the number of rating items. /// public int ItemCount { get { return (int)GetValue(ItemCountProperty); } set { SetValue(ItemCountProperty, value); } } /// /// Identifies the ItemCount dependency property. /// public static readonly DependencyProperty ItemCountProperty = DependencyProperty.Register( "ItemCount", typeof(int), typeof(Rating), new PropertyMetadata(0, OnItemCountChanged)); /// /// ItemCountProperty property changed handler. /// /// Rating that changed its ItemCount. /// Event arguments. private static void OnItemCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Rating source = d as Rating; int value = (int)e.NewValue; source.OnItemCountChanged(value); } /// /// This method is invoked when the items count property is changed. /// /// The new value. private void OnItemCountChanged(int newValue) { if (newValue < 0) { throw new ArgumentException(Properties.Resources.Rating_SetItemCount_ItemCountMustBeLargerThanOrEqualToZero); } int amountToAdd = newValue - this.Items.Count; if (amountToAdd > 0) { for (int cnt = 0; cnt < amountToAdd; cnt++) { this.Items.Add(new RatingItem()); } } else if (amountToAdd < 0) { for (int cnt = 0; cnt < Math.Abs(amountToAdd); cnt++) { this.Items.RemoveAt(this.Items.Count - 1); } } } #endregion public int ItemCount #region public bool IsReadOnly /// /// Gets or sets a value indicating whether the Rating is read-only. /// public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } /// /// Identifies the IsReadOnly dependency property. /// public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register( "IsReadOnly", typeof(bool), typeof(Rating), new PropertyMetadata(false, OnIsReadOnlyChanged)); /// /// IsReadOnlyProperty property changed handler. /// /// Rating that changed its IsReadOnly. /// Event arguments. private static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Rating source = (Rating)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsReadOnlyChanged(oldValue, newValue); } /// /// IsReadOnlyProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsReadOnlyChanged(bool oldValue, bool newValue) { Interaction.OnIsReadOnlyChanged(newValue); foreach (RatingItem ratingItem in GetRatingItems()) { ratingItem.IsReadOnly = newValue; } UpdateHoverStates(); } #endregion public bool IsReadOnly #region public RatingSelectionMode SelectionMode /// /// Gets or sets the selection mode. /// public RatingSelectionMode SelectionMode { get { return (RatingSelectionMode)GetValue(SelectionModeProperty); } set { SetValue(SelectionModeProperty, value); } } /// /// Identifies the SelectionMode dependency property. /// public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register( "SelectionMode", typeof(RatingSelectionMode), typeof(Rating), new PropertyMetadata(RatingSelectionMode.Continuous, OnSelectionModeChanged)); /// /// SelectionModeProperty property changed handler. /// /// Rating that changed its SelectionMode. /// Event arguments. private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Rating source = (Rating)d; RatingSelectionMode oldValue = (RatingSelectionMode)e.OldValue; RatingSelectionMode newValue = (RatingSelectionMode)e.NewValue; source.OnSelectionModeChanged(oldValue, newValue); } /// /// SelectionModeProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnSelectionModeChanged(RatingSelectionMode oldValue, RatingSelectionMode newValue) { UpdateDisplayValues(); } #endregion public RatingSelectionMode SelectionMode #region public double? Value /// /// Gets or sets the rating value. /// [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Value is the logical name for this property.")] [TypeConverter(typeof(NullableConverter))] public double? Value { get { return (double?)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } /// /// Identifies the Value dependency property. /// public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double?), typeof(Rating), new PropertyMetadata(new double?(), OnValueChanged)); /// /// ValueProperty property changed handler. /// /// Rating that changed its Value. /// Event arguments. private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Rating source = (Rating)d; double? oldValue = (double?)e.OldValue; double? newValue = (double?)e.NewValue; source.OnValueChanged(oldValue, newValue); } /// /// ValueProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnValueChanged(double? oldValue, double? newValue) { UpdateValues(); #if SILVERLIGHT RoutedPropertyChangedEventHandler handler = ValueChanged; if (handler != null) { handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue)); } #else RaiseEvent(new RoutedPropertyChangedEventArgs(oldValue, newValue, ValueChangedEvent)); #endif } /// /// Updates the control when the items change. /// /// Information about the event. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { EventHandler layoutUpdated = null; layoutUpdated = delegate { this.LayoutUpdated -= layoutUpdated; UpdateValues(); UpdateDisplayValues(); }; this.LayoutUpdated += layoutUpdated; this.ItemCount = this.Items.Count; base.OnItemsChanged(e); } /// /// This event is raised when the value of the rating is changed. /// public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(Rating)); /// /// This event is raised when the value of the rating is changed. /// public event RoutedPropertyChangedEventHandler ValueChanged { add { AddHandler(ValueChangedEvent, value); } remove { RemoveHandler(ValueChangedEvent, value); } } #endregion public double? Value /// /// Initializes the static members of the ColumnDataPoint class. /// static Rating() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Rating), new FrameworkPropertyMetadata(typeof(Rating))); } /// /// Initializes a new instance of the Rating control. /// public Rating() { this.Interaction = new InteractionHelper(this); } /// /// This method is invoked when the mouse enters the rating item. /// /// Information about the event. protected override void OnMouseEnter(MouseEventArgs e) { if (Interaction.AllowMouseEnter(e)) { Interaction.UpdateVisualStateBase(true); } base.OnMouseEnter(e); } /// /// This method is invoked when the mouse leaves the rating item. /// /// Information about the event. protected override void OnMouseLeave(MouseEventArgs e) { if (Interaction.AllowMouseLeave(e)) { Interaction.UpdateVisualStateBase(true); } base.OnMouseLeave(e); } /// /// Provides handling for the Rating's MouseLeftButtonDown event. /// /// Event arguments. protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (Interaction.AllowMouseLeftButtonDown(e)) { Interaction.OnMouseLeftButtonDownBase(); } base.OnMouseLeftButtonDown(e); } /// /// Provides handling for the Rating's MouseLeftButtonUp event. /// /// Event arguments. protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { if (Interaction.AllowMouseLeftButtonUp(e)) { Interaction.OnMouseLeftButtonUpBase(); } base.OnMouseLeftButtonUp(e); } /// /// Updates the values of the rating items. /// private void UpdateValues() { IList ratingItems = GetRatingItems().ToList(); RatingItem oldSelectedItem = this.GetSelectedRatingItem(); IEnumerable> itemAndWeights = EnumerableFunctions .Zip( ratingItems, ratingItems .Select(ratingItem => 1.0) .GetWeightedValues(Value.GetValueOrDefault()), (item, percent) => Tuple.Create(item, percent)); foreach (Tuple itemAndWeight in itemAndWeights) { itemAndWeight.First.Value = itemAndWeight.Second; } RatingItem newSelectedItem = this.GetSelectedRatingItem(); // Notify when the selection changes if (oldSelectedItem != newSelectedItem) { if (newSelectedItem != null && AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected)) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(newSelectedItem); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected); } } if (oldSelectedItem != null && AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(oldSelectedItem); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection); } } } if (HoveredRatingItem == null) { DisplayValue = Value.GetValueOrDefault(); } } /// /// Updates the value and actual value of the rating items. /// private void UpdateDisplayValues() { IList ratingItems = GetRatingItems().ToList(); IEnumerable> itemAndWeights = EnumerableFunctions .Zip( ratingItems, ratingItems .Select(ratingItem => 1.0) .GetWeightedValues(DisplayValue), (item, percent) => Tuple.Create(item, percent)); RatingItem selectedItem = null; Tuple selectedItemAndWeight = itemAndWeights.LastOrDefault(i => i.Second > 0.0); if (selectedItemAndWeight != null) { selectedItem = selectedItemAndWeight.First; } else { selectedItem = GetSelectedRatingItem(); } foreach (Tuple itemAndWeight in itemAndWeights) { if (SelectionMode == RatingSelectionMode.Individual && itemAndWeight.First != selectedItem) { itemAndWeight.First.DisplayValue = 0.0; } else { itemAndWeight.First.DisplayValue = itemAndWeight.Second; } } } /// /// Updates the hover states of the rating items. /// private void UpdateHoverStates() { if (HoveredRatingItem != null && !IsReadOnly) { IList ratingItems = GetRatingItems().ToList(); int indexOfItem = ratingItems.IndexOf(HoveredRatingItem); double total = ratingItems.Count(); double filled = indexOfItem + 1; this.DisplayValue = filled / total; for (int cnt = 0; cnt < ratingItems.Count; cnt++) { RatingItem ratingItem = ratingItems[cnt]; if (cnt <= indexOfItem && this.SelectionMode == RatingSelectionMode.Continuous) { VisualStates.GoToState(ratingItem, true, VisualStates.StateMouseOver); } else { IUpdateVisualState updateVisualState = (IUpdateVisualState)ratingItem; updateVisualState.UpdateVisualState(true); } } } else { this.DisplayValue = this.Value.GetValueOrDefault(); foreach (IUpdateVisualState updateVisualState in GetRatingItems().OfType()) { updateVisualState.UpdateVisualState(true); } } } /// /// This method returns a container for the item. /// /// A container for the item. protected override DependencyObject GetContainerForItemOverride() { return new RatingItem(); } /// /// Gets a value indicating whether the item is its own container. /// /// The item which may be a container. /// A value indicating whether the item is its own container. /// protected override bool IsItemItsOwnContainerOverride(object item) { return item is RatingItem; } /// /// This method prepares a container to host an item. /// /// The container. /// The item hosted in the container. protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { RatingItem ratingItem = (RatingItem)element; object defaultForegroundValue = ratingItem.ReadLocalValue(Control.ForegroundProperty); if (defaultForegroundValue == DependencyProperty.UnsetValue) { ratingItem.SetBinding(Control.ForegroundProperty, new Binding("Foreground") { Source = this }); } ratingItem.IsReadOnly = this.IsReadOnly; if (ratingItem.Style == null) { ratingItem.Style = this.ItemContainerStyle; } ratingItem.Click += RatingItemClick; ratingItem.MouseEnter += RatingItemMouseEnter; ratingItem.MouseLeave += RatingItemMouseLeave; ratingItem.ParentRating = this; base.PrepareContainerForItemOverride(element, item); } /// /// This method clears a container used to host an item. /// /// The container that hosts the item. /// The item hosted in the container. protected override void ClearContainerForItemOverride(DependencyObject element, object item) { RatingItem ratingItem = (RatingItem)element; ratingItem.Click -= RatingItemClick; ratingItem.MouseEnter -= RatingItemMouseEnter; ratingItem.MouseLeave -= RatingItemMouseLeave; ratingItem.ParentRating = null; if (ratingItem == HoveredRatingItem) { HoveredRatingItem = null; UpdateDisplayValues(); UpdateHoverStates(); } base.ClearContainerForItemOverride(element, item); } /// /// This method is invoked when a rating item's mouse enter event is /// invoked. /// /// The source of the event. /// Information about the event. private void RatingItemMouseEnter(object sender, MouseEventArgs e) { HoveredRatingItem = (RatingItem)sender; UpdateHoverStates(); } /// /// This method is invoked when a rating item's mouse leave event is /// invoked. /// /// The source of the event. /// Information about the event. private void RatingItemMouseLeave(object sender, MouseEventArgs e) { HoveredRatingItem = null; UpdateDisplayValues(); UpdateHoverStates(); } /// /// Returns a sequence of rating items. /// /// A sequence of rating items. internal IEnumerable GetRatingItems() { // The query above returns null in WPF // Either way, WPF will already contain the RatingItem objects in the Items collection. return this.Items.Cast(); } /// /// Selects a rating item. /// /// The selected rating item. internal void SelectRatingItem(RatingItem selectedRatingItem) { if (!this.IsReadOnly) { IList ratingItems = GetRatingItems().ToList(); IEnumerable weights = ratingItems.Select(ratingItem => 1.0); double total = ratingItems.Count(); double percent; if (total != 0) { percent = weights.Take(ratingItems.IndexOf(selectedRatingItem) + 1).Sum() / total; this.Value = percent; } } } /// /// This method is raised when a rating item value is selected. /// /// The source of the event. /// Information about the event. private void RatingItemClick(object sender, RoutedEventArgs e) { if (!this.IsReadOnly) { RatingItem item = (RatingItem)sender; OnRatingItemValueSelected(item, 1.0); } } /// /// Returns the selected rating item. /// /// The selected rating item. private RatingItem GetSelectedRatingItem() { return this.GetRatingItems().LastOrDefault(ratingItem => ratingItem.Value > 0.0); } /// /// This method is invoked when the rating item value is changed. /// /// The rating item that has changed. /// The new value. protected virtual void OnRatingItemValueSelected(RatingItem ratingItem, double newValue) { List ratingItems = GetRatingItems().ToList(); double total = ratingItems.Count(); double value = (ratingItems .Take(ratingItems.IndexOf(ratingItem)) .Count() + newValue) / total; this.Value = value; } /// /// Returns a RatingItemAutomationPeer for use by the Silverlight /// automation infrastructure. /// /// A RatingItemAutomationPeer object for the RatingItem. protected override AutomationPeer OnCreateAutomationPeer() { return new RatingAutomationPeer(this); } /// /// Provides handling for the /// event when a key /// is pressed while the control has focus. /// /// /// A that contains /// the event data. /// /// /// is null. /// [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Complexity metric is inflated by the switch statements")] protected override void OnKeyDown(KeyEventArgs e) { if (!Interaction.AllowKeyDown(e)) { return; } base.OnKeyDown(e); if (e.Handled) { return; } switch (e.Key) { case Key.Left: { RatingItem ratingItem = null; if (Application.Current.CheckAccess()) ratingItem = FocusManager.GetFocusedElement(Application.Current.MainWindow) as RatingItem; if (ratingItem != null) { ratingItem = GetRatingItemAtOffsetFrom(ratingItem, -1); } else { ratingItem = GetRatingItems().FirstOrDefault(); } if (ratingItem != null) { if (ratingItem.Focus()) { e.Handled = true; } } } break; case Key.Right: { RatingItem ratingItem = null; if (Application.Current.CheckAccess()) ratingItem = FocusManager.GetFocusedElement(Application.Current.MainWindow) as RatingItem; if (ratingItem != null) { ratingItem = GetRatingItemAtOffsetFrom(ratingItem, 1); } else { ratingItem = GetRatingItems().FirstOrDefault(); } if (ratingItem != null) { if (ratingItem.Focus()) { e.Handled = true; } } } break; case Key.Add: { if (!this.IsReadOnly) { RatingItem ratingItem = GetSelectedRatingItem(); if (ratingItem != null) { ratingItem = GetRatingItemAtOffsetFrom(ratingItem, 1); } else { ratingItem = GetRatingItems().FirstOrDefault(); } if (ratingItem != null) { ratingItem.SelectValue(); e.Handled = true; } } } break; case Key.Subtract: { if (!this.IsReadOnly) { RatingItem ratingItem = GetSelectedRatingItem(); if (ratingItem != null) { ratingItem = GetRatingItemAtOffsetFrom(ratingItem, -1); } if (ratingItem != null) { ratingItem.SelectValue(); e.Handled = true; } } } break; } } /// /// Gets a rating item at a certain index offset from another /// rating item. /// /// The rating item. /// The rating item at an offset from the /// index of the rating item. /// The rating item at the offset. private RatingItem GetRatingItemAtOffsetFrom(RatingItem ratingItem, int offset) { IList ratingItems = GetRatingItems().ToList(); int index = ratingItems.IndexOf(ratingItem); if (index == -1) { return null; } index += offset; if (index >= 0 && index < ratingItems.Count) { ratingItem = ratingItems[index]; } else { ratingItem = null; } return ratingItem; } /// /// Updates the visual state. /// /// A value indicating whether to use transitions. void IUpdateVisualState.UpdateVisualState(bool useTransitions) { Interaction.UpdateVisualStateBase(useTransitions); } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/RatingItem.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows.Automation.Peers; using System.Windows.Controls.Primitives; using System.Windows.Input; namespace System.Windows.Controls { /// /// An item used in a rating control. /// /// Preview [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateReadOnly, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = StateFilled, GroupName = GroupFill)] [TemplateVisualState(Name = StateEmpty, GroupName = GroupFill)] [TemplateVisualState(Name = StatePartial, GroupName = GroupFill)] public class RatingItem : ButtonBase, IUpdateVisualState { /// /// The state in which the item is filled. /// private const string StateFilled = "Filled"; /// /// The state in which the item is empty. /// private const string StateEmpty = "Empty"; /// /// The group that contains fill states. /// private const string GroupFill = "FillStates"; /// /// The state in which the item is partially filled. /// private const string StatePartial = "Partial"; /// /// The interaction helper used to get the common states working. /// private InteractionHelper _interactionHelper; #region public double DisplayValue /// /// A value indicating whether the actual value is being set. /// private bool _settingDisplayValue; /// /// Gets the actual value. /// public double DisplayValue { get { return (double)GetValue(DisplayValueProperty); } internal set { _settingDisplayValue = true; try { SetValue(DisplayValueProperty, value); } finally { _settingDisplayValue = false; } } } /// /// Identifies the DisplayValue dependency property. /// public static readonly DependencyProperty DisplayValueProperty = DependencyProperty.Register( "DisplayValue", typeof(double), typeof(RatingItem), new PropertyMetadata(0.0, OnDisplayValueChanged)); /// /// DisplayValueProperty property changed handler. /// /// RatingItem that changed its DisplayValue. /// Event arguments. private static void OnDisplayValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RatingItem source = (RatingItem)d; source.OnDisplayValueChanged((double) e.OldValue, (double) e.NewValue); } /// /// DisplayValueProperty property changed handler. /// /// The old value. /// The new value. private void OnDisplayValueChanged(double oldValue, double newValue) { if (!_settingDisplayValue) { _settingDisplayValue = true; this.DisplayValue = oldValue; throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.InvalidAttemptToChangeReadOnlyProperty, "DisplayValue")); } else { if (newValue <= 0.0) { VisualStates.GoToState(this, true, StateEmpty); } else if (newValue >= 1.0) { VisualStates.GoToState(this, true, StateFilled); } else { VisualStates.GoToState(this, true, StatePartial); } } } #endregion public double DisplayValue #region public bool IsReadOnly /// /// A value indicating whether the read only value is being set. /// private bool _settingIsReadOnly; /// /// Gets a value indicating whether the control is read-only. /// public bool IsReadOnly { get { return (bool) GetValue(IsReadOnlyProperty); } internal set { _settingIsReadOnly = true; try { SetValue(IsReadOnlyProperty, value); } finally { _settingIsReadOnly = false; } } } /// /// Identifies the IsReadOnly dependency property. /// public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register( "IsReadOnly", typeof(bool), typeof(RatingItem), new PropertyMetadata(false, OnIsReadOnlyChanged)); /// /// IsReadOnlyProperty property changed handler. /// /// RatingItem that changed its IsReadOnly. /// Event arguments. private static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RatingItem source = (RatingItem)d; bool oldValue = (bool)e.OldValue; bool newValue = (bool)e.NewValue; source.OnIsReadOnlyChanged(oldValue, newValue); } /// /// IsReadOnlyProperty property changed handler. /// /// Old value. /// New value. protected virtual void OnIsReadOnlyChanged(bool oldValue, bool newValue) { if (!_settingIsReadOnly) { _settingIsReadOnly = true; this.IsReadOnly = oldValue; throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.InvalidAttemptToChangeReadOnlyProperty, "IsReadOnly")); } else { _interactionHelper.OnIsReadOnlyChanged(newValue); } } #endregion public bool IsReadOnly /// /// Gets or sets the parent rating of this rating item. /// internal Rating ParentRating { get; set; } #region public double Value /// /// Gets or sets the value property. /// [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Value is the logical name for this property.")] internal double Value { get { return (double)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } /// /// Identifies the Value dependency property. /// internal static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(RatingItem), new PropertyMetadata(0.0)); /// /// Selects a value and raises the value selected event. /// internal void SelectValue() { if (!this.IsReadOnly) { this.Value = 1.0; OnClick(); } } #endregion public double Value /// /// Initializes the static members of the ColumnDataPoint class. /// static RatingItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(RatingItem), new FrameworkPropertyMetadata(typeof(RatingItem))); } /// /// Initializes a new instance of the RatingItem class. /// public RatingItem() { _interactionHelper = new InteractionHelper(this); } /// /// Provides handling for the RatingItem's MouseLeftButtonDown event. /// /// Event arguments. protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (_interactionHelper.AllowMouseLeftButtonDown(e)) { _interactionHelper.OnMouseLeftButtonDownBase(); } base.OnMouseLeftButtonDown(e); } /// /// Provides handling for the RatingItem's MouseLeftButtonUp event. /// /// Event arguments. protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { if (_interactionHelper.AllowMouseLeftButtonUp(e)) { _interactionHelper.OnMouseLeftButtonUpBase(); } base.OnMouseLeftButtonUp(e); } /// /// This method is invoked when the mouse enters the rating item. /// /// Information about the event. protected override void OnMouseEnter(MouseEventArgs e) { if (_interactionHelper.AllowMouseEnter(e)) { _interactionHelper.UpdateVisualStateBase(true); } base.OnMouseEnter(e); } /// /// This method is invoked when the mouse leaves the rating item. /// /// Information about the event. protected override void OnMouseLeave(MouseEventArgs e) { if (_interactionHelper.AllowMouseLeave(e)) { _interactionHelper.UpdateVisualStateBase(true); } base.OnMouseLeave(e); } /// /// Sets the value to 1.0 when clicked. /// protected override void OnClick() { base.OnClick(); } /// /// Updates the visual state. /// /// A value indicating whether to use /// transitions. void IUpdateVisualState.UpdateVisualState(bool useTransitions) { _interactionHelper.UpdateVisualStateBase(useTransitions); } /// /// Returns a AccordionItemAutomationPeer for use by the Silverlight /// automation infrastructure. /// /// A AccordionItemAutomationPeer object for the AccordionItem. protected override AutomationPeer OnCreateAutomationPeer() { return new RatingItemAutomationPeer(this); } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/RatingSelectionMode.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// This type is used to determine the state of the item selected and the /// previous items. /// /// Preview public enum RatingSelectionMode { /// /// All items before the selected ones are selected. /// Continuous, /// /// Only the item selected is visually distinguished. /// Individual } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/Tuple.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// A structure that groups two values. /// /// The type of the first value. /// The type of the second value. internal class Tuple { /// /// Gets the first value. /// public T0 First { get; private set; } /// /// Gets the second value. /// public T1 Second { get; private set; } /// /// Initializes a new instance of the Tuple structure. /// /// The first value. /// The second value. public Tuple(T0 first, T1 second) { First = first; Second = second; } } } ================================================ FILE: WpfToolkit/Input/Rating/System/Windows/Controls/TupleExtensions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// A set of tuple functions. /// internal static class Tuple { /// /// A method to create tuples. /// /// The type of the first item. /// The type of the second item. /// The type of the first argument. /// The type of the second argument. /// The tuple to return. public static Tuple Create(T0 arg0, T1 arg1) { return new Tuple(arg0, arg1); } } } ================================================ FILE: WpfToolkit/Input/Themes/Generic.xaml ================================================  ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Automation/Peers/AccordionAutomationPeer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Windows.Automation.Provider; using System.Windows.Controls; namespace System.Windows.Automation.Peers { /// /// Exposes Accordion types to UI Automation. /// /// Preview public class AccordionAutomationPeer : ItemsControlAutomationPeer, ISelectionProvider { /// /// Gets the Accordion that owns this AccordionAutomationPeer. /// /// The accordion. private Accordion OwnerAccordion { get { return (Accordion)Owner; } } /// /// Initializes a new instance of the class. /// /// The Accordion that is associated with this /// AccordionAutomationPeer. public AccordionAutomationPeer(Accordion owner) : base(owner) { } /// /// Gets the name of the Accordion that is associated with this /// AccordionAutomationPeer. This method is called by GetClassName. /// /// The name Accordion. protected override string GetClassNameCore() { return "Accordion"; } /// /// Gets the control type for the Accordion that is associated /// with this AccordionAutomationPeer. This method is called by /// GetAutomationControlType. /// /// List AutomationControlType. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.List; } /// /// Gets the control pattern for the Accordion that is associated /// with this AccordionAutomationPeer. /// /// The desired PatternInterface. /// The desired AutomationPeer or null. public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.Selection) { return this; } return base.GetPattern(patternInterface); } /// /// Gets the collection of child elements of /// the that is /// associated with this . /// /// /// A collection of AccordionItemAutomationPeer elements, or null if the /// Accordion that is associated with this AccordionAutomationPeer is /// empty. /// [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "Required by automation")] protected override List GetChildrenCore() { Accordion owner = OwnerAccordion; ItemCollection items = owner.Items; if (items.Count <= 0) { return null; } List peers = new List(items.Count); for (int i = 0; i < items.Count; i++) { AccordionItem element = owner.ItemContainerGenerator.ContainerFromIndex(i) as AccordionItem; if (element != null) { peers.Add(FromElement(element) ?? CreatePeerForElement(element)); } } return peers; } /// /// Gets a value indicating whether the UI Automation provider /// allows more than one child element to be selected concurrently. /// /// true if multiple selection is allowed; otherwise, false. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// public bool CanSelectMultiple { get { return OwnerAccordion.SelectionMode == AccordionSelectionMode.OneOrMore || OwnerAccordion.SelectionMode == AccordionSelectionMode.ZeroOrMore; } } /// /// Retrieves a UI Automation provider for each child element that is /// selected. /// /// An array of UI Automation providers. /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// public IRawElementProviderSimple[] GetSelection() { Accordion owner = OwnerAccordion; List selection = new List(owner.SelectedIndices.Count); foreach (int index in owner.SelectedIndices) { AccordionItem item = owner.ItemContainerGenerator.ContainerFromIndex(index) as AccordionItem; if (item != null) { AutomationPeer peer = FromElement(item); if (peer != null) { selection.Add(ProviderFromPeer(peer)); } } } return selection.ToArray(); } /// /// Gets a value indicating whether the UI Automation provider /// requires at least one child element to be selected. /// /// true if selection is required; otherwise, false. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// public bool IsSelectionRequired { get { return OwnerAccordion.SelectionMode == AccordionSelectionMode.One || OwnerAccordion.SelectionMode == AccordionSelectionMode.OneOrMore; } } #if !SILVERLIGHT /// /// Exposes a data item to UI Automation. /// /// The item to expose /// The UI automation object associated with the item protected override ItemAutomationPeer CreateItemAutomationPeer(object item) { return new AccordionItemAutomationPeer(item, this); } #endif } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Automation/Peers/AccordionItemAutomationPeer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Windows.Automation.Provider; using System.Windows.Controls; [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.IExpandCollapseProvider.Collapse()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.IExpandCollapseProvider.Expand()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.IExpandCollapseProvider.ExpandCollapseState", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.AddToSelection()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.IsSelected", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.RemoveFromSelection()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.Select()", Justification = "Required for subset compat with WPF")] [assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Automation.Peers.AccordionItemAutomationPeer.#System.Windows.Automation.Provider.ISelectionItemProvider.SelectionContainer", Justification = "Required for subset compat with WPF")] namespace System.Windows.Automation.Peers { /// /// Exposes AccordionItem types to UI Automation. /// /// Preview #if SILVERLIGHT public class AccordionItemAutomationPeer : FrameworkElementAutomationPeer, IExpandCollapseProvider, ISelectionItemProvider #else public class AccordionItemAutomationPeer : ItemAutomationPeer, IExpandCollapseProvider, ISelectionItemProvider #endif { /// /// Gets the AccordionItem that owns this AccordionItemAutomationPeer. /// private AccordionItem OwnerAccordionItem { #if SILVERLIGHT get { return (AccordionItem)Owner; } #else get { return base.Item as AccordionItem; } #endif } #if SILVERLIGHT /// /// Initializes a new instance of the AccordionAutomationPeer class. /// /// /// The Accordion that is associated with this /// AccordionAutomationPeer. /// public AccordionItemAutomationPeer(AccordionItem owner) : base(owner) { } #else /// /// Initializes a new instance of the AccordionAutomationPeer class. /// /// /// The item associated with this AutomationPeer /// /// /// The Accordion that is associated with this item. /// public AccordionItemAutomationPeer(object item, ItemsControlAutomationPeer itemsControlAutomationPeer) : base(item, itemsControlAutomationPeer) { } #endif /// /// Gets the control type for the AccordionItem that is associated /// with this AccordionItemAutomationPeer. This method is called by /// GetAutomationControlType. /// /// Custom AutomationControlType. protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.ListItem; } /// /// Gets the name of the AccordionItem that is associated with this /// AccordionItemAutomationPeer. This method is called by GetClassName. /// /// The name AccordionItem. protected override string GetClassNameCore() { return "AccordionItem"; } /// /// Gets the control pattern for the AccordionItem that is associated /// with this AccordionItemAutomationPeer. /// /// The desired PatternInterface. /// The desired AutomationPeer or null. public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.ExpandCollapse || patternInterface == PatternInterface.SelectionItem) { return this; } return null; } /// /// Gets the state (expanded or collapsed) of the Accordion. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState { get { return OwnerAccordionItem.IsSelected ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; } } /// /// Collapses the AccordionItem. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void IExpandCollapseProvider.Collapse() { if (!IsEnabled()) { throw new ElementNotEnabledException(); } AccordionItem owner = OwnerAccordionItem; if (owner.IsLocked) { throw new InvalidOperationException(Controls.Properties.Resources.Automation_OperationCannotBePerformed); } owner.IsSelected = false; } /// /// Expands the AccordionItem. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void IExpandCollapseProvider.Expand() { if (!IsEnabled()) { throw new ElementNotEnabledException(); } AccordionItem owner = OwnerAccordionItem; if (owner.IsLocked) { throw new InvalidOperationException(Controls.Properties.Resources.Automation_OperationCannotBePerformed); } owner.IsSelected = true; } /// /// Adds the AccordionItem to the collection of selected items. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void ISelectionItemProvider.AddToSelection() { AccordionItem owner = OwnerAccordionItem; Accordion parent = owner.ParentAccordion; if (parent == null) { throw new InvalidOperationException(Controls.Properties.Resources.Automation_OperationCannotBePerformed); } parent.SelectedItems.Add(owner); } /// /// Gets a value indicating whether the Accordion is selected. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// bool ISelectionItemProvider.IsSelected { get { return OwnerAccordionItem.IsSelected; } } /// /// Removes the current Accordion from the collection of selected /// items. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void ISelectionItemProvider.RemoveFromSelection() { AccordionItem owner = OwnerAccordionItem; Accordion parent = owner.ParentAccordion; if (parent == null) { throw new InvalidOperationException(Controls.Properties.Resources.Automation_OperationCannotBePerformed); } parent.SelectedItems.Remove(owner); } /// /// Clears selection from currently selected items and then proceeds to /// select the current Accordion. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// void ISelectionItemProvider.Select() { OwnerAccordionItem.IsSelected = true; } /// /// Gets the UI Automation provider that implements ISelectionProvider /// and acts as the container for the calling object. /// /// /// This API supports the .NET Framework infrastructure and is not /// intended to be used directly from your code. /// IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { Accordion parent = OwnerAccordionItem.ParentAccordion; if (parent != null) { #if SILVERLIGHT AutomationPeer peer = FromElement(parent); #else AutomationPeer peer = UIElementAutomationPeer.FromElement(parent); #endif if (peer != null) { return ProviderFromPeer(peer); } } return null; } } } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Automation/Peers/AccordionItemWrapperAutomationPeer.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Windows.Automation.Provider; using System.Windows.Controls; namespace System.Windows.Automation.Peers { /// /// Wraps an . /// public class AccordionItemWrapperAutomationPeer : FrameworkElementAutomationPeer { /// /// Constructor /// /// The to wrap. public AccordionItemWrapperAutomationPeer(AccordionItem item) : base(item) { } } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/Accordion.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Windows.Automation.Peers; using System.Windows.Data; using System.Windows.Controls.Primitives; using System.Windows.Input; namespace System.Windows.Controls { /// /// Represents a collection of collapsed and expanded AccordionItem controls. /// /// Preview [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(AccordionItem))] [StyleTypedProperty(Property = AccordionButtonStyleName, StyleTargetType = typeof(AccordionButton))] [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] public class Accordion : ItemsControl, IUpdateVisualState { /// /// The items that are currently waiting to perform an action. /// /// An action can be expanding, resizing or collapsing. private readonly List _scheduledActions; /// /// The name used to indicate AccordionButtonStyle property. /// private const string AccordionButtonStyleName = "AccordionButtonStyle"; /// /// Determines whether the SelectedItemsProperty may be written. /// private bool _isAllowedToWriteSelectedItems; /// /// Determines whether the SelectedIndicesProperty may be written. /// private bool _isAllowedToWriteSelectedIndices; /// /// Indicates that changes to the SelectedIndices collection should /// be ignored. /// private bool _isIgnoringSelectedIndicesChanges; /// /// Indicates that changes to the SelectedItems collection should /// be ignored. /// private bool _isIgnoringSelectedItemsChanges; /// /// Determines whether we are currently in the SelectedItems Collection /// Changed handling. /// private bool _isInSelectedItemsCollectionChanged; /// /// Determines whether we are currently in the SelectedIndices Collection /// Changed handling. /// private bool _isInSelectedIndicesCollectionChanged; /// /// The item that is currently visually performing an action. /// /// An action can be expanding, resizing or collapsing. private AccordionItem _currentActioningItem; /// /// Gets the ItemsControlHelper that is associated with this control. /// internal ItemsControlHelper ItemsControlHelper { get; private set; } /// /// Gets a value indicating whether this instance is currently resizing. /// /// True if this instance is resizing; otherwise, false. internal bool IsResizing { get; private set; } /// /// Gets or sets the helper that provides all of the standard /// interaction functionality. /// private InteractionHelper Interaction { get; set; } #region public ExpandDirection ExpandDirection /// /// Gets or sets the ExpandDirection property of each /// AccordionItem in the Accordion control and the direction in which /// the Accordion does layout. /// /// Setting the ExpandDirection will set the expand direction /// on the accordionItems. public ExpandDirection ExpandDirection { get { return (ExpandDirection)GetValue(ExpandDirectionProperty); } set { SetValue(ExpandDirectionProperty, value); } } /// /// Identifies the ExpandDirection dependency property. /// public static readonly DependencyProperty ExpandDirectionProperty = DependencyProperty.Register( "ExpandDirection", typeof(ExpandDirection), typeof(Accordion), new PropertyMetadata(ExpandDirection.Down, OnExpandDirectionPropertyChanged)); /// /// ExpandDirectionProperty property changed handler. /// /// Accordion that changed its ExpandDirection. /// Event arguments. private static void OnExpandDirectionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion source = (Accordion)d; ExpandDirection expandDirection = (ExpandDirection)e.NewValue; if (expandDirection != ExpandDirection.Down && expandDirection != ExpandDirection.Up && expandDirection != ExpandDirection.Left && expandDirection != ExpandDirection.Right) { // revert to old value source.SetValue(ExpandDirectionProperty, e.OldValue); string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.Accordion_OnExpandDirectionPropertyChanged_InvalidValue, expandDirection); throw new ArgumentOutOfRangeException("e", message); } // force this change to all AccordionItems for (int i = 0; i < source.Items.Count; i++) { AccordionItem accordionItem = source.ItemContainerGenerator.ContainerFromIndex(i) as AccordionItem; if (accordionItem != null) { accordionItem.ExpandDirection = expandDirection; } } // set panel to align to the change source.SetPanelOrientation(); // schedule a layout pass after this panel has had time to rearrange. source.Dispatcher.BeginInvoke(new Action(source.LayoutChildren)); } #endregion public ExpandDirection ExpandDirection #region public AccordionSelectionMode SelectionMode /// /// Gets or sets the AccordionSelectionMode used to determine the minimum /// and maximum selected AccordionItems allowed in the Accordion. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi", Justification = "WPF has a MultiSelector class that will be used in the future.")] public AccordionSelectionMode SelectionMode { get { return (AccordionSelectionMode)GetValue(SelectionModeProperty); } set { SetValue(SelectionModeProperty, value); } } /// /// Identifies the SelectionMode dependency property. /// public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register( "SelectionMode", typeof(AccordionSelectionMode), typeof(Accordion), new PropertyMetadata(AccordionSelectionMode.One, OnSelectionModePropertyChanged)); /// /// SelectionModeProperty property changed handler. /// /// Accordion that changed its SelectionMode. /// Event arguments. private static void OnSelectionModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion source = (Accordion)d; AccordionSelectionMode newValue = (AccordionSelectionMode)e.NewValue; if (newValue != AccordionSelectionMode.One && newValue != AccordionSelectionMode.OneOrMore && newValue != AccordionSelectionMode.ZeroOrMore && newValue != AccordionSelectionMode.ZeroOrOne) { // revert to old value source.SetValue(SelectionModeProperty, e.OldValue); string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.Accordion_OnSelectionModePropertyChanged_InvalidValue, newValue); throw new ArgumentOutOfRangeException("e", message); } // unlock all items // a selectionmode change is expected to change the locks. for (int i = 0; i < source.Items.Count; i++) { AccordionItem item = source.ItemContainerGenerator.ContainerFromIndex(i) as AccordionItem; if (item != null) { item.IsLocked = false; } } // single selection coercion if (source.IsMinimumOneSelected) { // a minimum of one item should be selected if (source.GetValue(SelectedItemProperty) == null && source.Items.Count > 0) { // select first accordionitem source.SetValue(SelectedItemProperty, source.Items[0]); } } // multi selection coeercion if (source.IsMaximumOneSelected) { // allow at most one item. if (source.SelectedIndices.Count > 1) { // make copy of collection, since it will be modified List indices = source.SelectedIndices.ToList(); foreach (int index in indices) { // unselect all items except the currently selected item. if (index != source.SelectedIndex) { source.UnselectItem(index, null); } } } } // re-evaluate the locking status of the items in this new configuration source.SetLockedProperties(); } /// /// Gets a value indicating whether at least one item is selected at /// all times. /// private bool IsMinimumOneSelected { get { return SelectionMode == AccordionSelectionMode.One || SelectionMode == AccordionSelectionMode.OneOrMore; } } /// /// Gets a value indicating whether at most one item is selected at all times. /// private bool IsMaximumOneSelected { get { return SelectionMode == AccordionSelectionMode.One || SelectionMode == AccordionSelectionMode.ZeroOrOne; } } #endregion public AccordionSelectionMode SelectionMode #region public object SelectedItem /// /// Gets or sets the selected item. /// /// /// The default value is null. /// When multiple items are allowed (IsMaximumOneSelected false), /// return the first of the selectedItems. /// public object SelectedItem { get { return GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } /// /// Identifies the SelectedItem dependency property. /// public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( "SelectedItem", typeof(object), typeof(Accordion), new PropertyMetadata(null, OnSelectedItemPropertyChanged)); /// /// SelectedItemProperty property changed handler. /// /// Accordion that changed its SelectedItem. /// Event arguments. private static void OnSelectedItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion source = (Accordion)d; object oldValue = e.OldValue; object newValue = e.NewValue; object[] newValues = newValue == null ? new object[0] : new[] { newValue }; object[] oldValues = oldValue == null ? new object[0] : new[] { oldValue }; if (oldValue != null && oldValue.Equals(newValue)) { // when value types are used as items, there is a possibility of getting a change notification. #if SILVERLIGHT source.OnSelectedItemChanged(new SelectionChangedEventArgs(oldValues, newValues)); #else source.OnSelectedItemChanged(new SelectionChangedEventArgs(SelectionChangedEvent, oldValues, newValues)); #endif return; } if (!source.IsValidItemForSelection(newValue)) { // reset to oldvalue source._selectedItemNestedLevel++; source.SetValue(SelectedItemProperty, oldValue); source._selectedItemNestedLevel--; } else if (source._selectedItemNestedLevel == 0) { if (newValue == null) { source.SelectedIndex = -1; } else { // be cautious about choosing a new index. int currentIndex = source.SelectedIndex; // use current SelectedIndex if possible if (currentIndex < 0 || currentIndex > source.Items.Count || !newValue.Equals(source.Items[currentIndex])) { // use an index out of SelectedIndices if possible // or fallback to finding the index in the ItemsCollection IEnumerable validIndices = source.SelectedIndices.Where(i => i >= 0 && i < source.Items.Count && newValue.Equals(source.Items[i])); currentIndex = validIndices.Count() > 0 ? validIndices.First() : source.Items.IndexOf(newValue); } source.SelectedIndex = currentIndex; } #if SILVERLIGHT source.OnSelectedItemChanged(new SelectionChangedEventArgs(oldValues, newValues)); #else source.OnSelectedItemChanged(new SelectionChangedEventArgs(SelectionChangedEvent, oldValues, newValues)); #endif } } /// /// Determines whether the new value can be selected. /// /// The new value. /// /// True if this item can be selected; otherwise, false. /// private bool IsValidItemForSelection(object newValue) { // setting to null is supported in some cases if (newValue == null) { // we can always return something since null is not a valid item. // if accordion allows no selection, null is accepted // if there are currently no items, null is accepted return (IsMinimumOneSelected == false || Items.Count == 0); } // item should be contained inside the items collection. return Items.OfType().Contains(newValue); } /// /// Nested level for SelectedItemCoercion. /// private int _selectedItemNestedLevel; #endregion public object SelectedItem #region public int SelectedIndex /// /// Gets or sets the index of the currently selected AccordionItem. /// public int SelectedIndex { get { return (int)GetValue(SelectedIndexProperty); } set { SetValue(SelectedIndexProperty, value); } } /// /// Identifies the SelectedIndex dependency property. /// public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register( "SelectedIndex", typeof(int), typeof(Accordion), new PropertyMetadata(-1, OnSelectedIndexPropertyChanged)); /// /// SelectedIndexProperty property changed handler. /// /// Accordion that changed its SelectedIndex. /// Event arguments. private static void OnSelectedIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion source = (Accordion)d; int oldValue = (int)e.OldValue; int newValue = (int)e.NewValue; // SelectedIndex will be changed when modifying the SelectionCollections. // Those should not trigger changes in the SelectedIndex. if (source._isIgnoringSelectedIndicesChanges) { return; } if (!source.IsValidIndexForSelection(newValue)) { // oldvalue might not be valid anymore (because of items removed from collection) if (source.IsValidIndexForSelection(oldValue)) { // oldvalue is valid, repress events source._selectedIndexNestedLevel++; source.SetValue(SelectedIndexProperty, oldValue); source._selectedIndexNestedLevel--; } else { // select new source.SetValue(SelectedIndexProperty, source.ProposeSelectedIndexCandidate(newValue)); } } else if (source._selectedIndexNestedLevel == 0) { // synchronize with SelectedItem #if SILVERLIGHT source.SelectedItem = source.Items.ElementAtOrDefault(newValue); #else // In .NET 3.5, ElementAtOrDefault will throw an exception when newValue is out of bounds. // Avoid the exception by explicitly checking the value is within the bounds source.SelectedItem = (newValue >= 0 && newValue < source.Items.Count) ? source.Items[newValue] : null; #endif // SelectedIndex is responsible for kicking off the real work. source.ChangeSelectedIndex(oldValue, newValue); } } /// /// Determines whether the new value can be selected. /// /// The new value. /// /// True if this item can be selected; otherwise, false. /// private bool IsValidIndexForSelection(int newValue) { // setting to null is supported in some cases if (newValue == -1) { // we can always return something since null is not a valid item. // if accordion allows no selection, null is accepted // if there are currently no items, null is accepted return (IsMinimumOneSelected == false || Items.Count == 0); } // index should be contained inside the items collection. return newValue >= 0 && newValue < Items.Count; } /// /// Coercion level. /// private int _selectedIndexNestedLevel; #endregion public int SelectedIndex #region public SelectionSequence SelectionSequence /// /// Gets or sets the SelectionSequence used to determine /// the order of AccordionItem selection. /// public SelectionSequence SelectionSequence { get { return (SelectionSequence)GetValue(SelectionSequenceProperty); } set { SetValue(SelectionSequenceProperty, value); } } /// /// Identifies the SelectionSequence dependency property. /// public static readonly DependencyProperty SelectionSequenceProperty = DependencyProperty.Register( "SelectionSequence", typeof(SelectionSequence), typeof(Accordion), new PropertyMetadata(SelectionSequence.Simultaneous, OnSelectionSequencePropertyChanged)); /// /// Called when SelectionSequenceProperty changed. /// /// Accordion that changed its SelectionSequence property. /// The /// instance containing the event data. private static void OnSelectionSequencePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { SelectionSequence newValue = (SelectionSequence)e.NewValue; if (newValue != SelectionSequence.CollapseBeforeExpand && newValue != SelectionSequence.Simultaneous) { // revert to old value d.SetValue(Accordion.SelectionSequenceProperty, e.OldValue); string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.Accordion_OnSelectionSequencepropertyChanged_InvalidValue, newValue); throw new ArgumentOutOfRangeException("e", message); } } #endregion public SelectionSequence SelectionSequence #region public IList SelectedItems /// /// Gets the selected items. /// /// Does not allow setting. public IList SelectedItems { get { return GetValue(SelectedItemsProperty) as IList; } private set { _isAllowedToWriteSelectedItems = true; SetValue(SelectedItemsProperty, value); _isAllowedToWriteSelectedItems = false; } } /// /// Identifies the SelectedItems dependency property. /// public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register( "SelectedItems", typeof(IList), typeof(Accordion), new PropertyMetadata(OnSelectedItemsChanged)); /// /// Property changed handler of SelectedItems. /// /// Accordion that changed the collection. /// Event arguments. private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion accordion = (Accordion)d; if (!accordion._isAllowedToWriteSelectedItems) { // revert to old value accordion.SelectedItems = e.OldValue as IList; throw new InvalidOperationException(Properties.Resources.Accordion_OnSelectedItemsChanged_InvalidWrite); } } #endregion public IList SelectedItems #region public IList SelectedIndices /// /// Gets the indices of the currently selected AccordionItems. /// [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Indices", Justification = "Framework uses indices.")] public IList SelectedIndices { get { return GetValue(SelectedIndicesProperty) as IList; } private set { _isAllowedToWriteSelectedIndices = true; SetValue(SelectedIndicesProperty, value); _isAllowedToWriteSelectedIndices = false; } } /// /// Identifies the SelectedIndices dependency property. /// [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Indices", Justification = "Framework uses indices.")] public static readonly DependencyProperty SelectedIndicesProperty = DependencyProperty.Register( "SelectedIndices", typeof(IList), typeof(Accordion), new PropertyMetadata(null, OnSelectedIndicesChanged)); /// /// Property changed handler of SelectedIndices. /// /// Accordion that changed the collection. /// Event arguments. private static void OnSelectedIndicesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion accordion = (Accordion)d; if (!accordion._isAllowedToWriteSelectedIndices) { // revert to old value accordion.SelectedIndices = e.OldValue as IList; throw new InvalidOperationException(Properties.Resources.Accordion_OnSelectedIndicesChanged_InvalidWrite); } } #endregion public IList SelectedIndices #if SILVERLIGHT #region public Style ItemContainerStyle /// /// Gets or sets the Style that is applied to the container element /// generated for each item. /// public Style ItemContainerStyle { get { return GetValue(ItemContainerStyleProperty) as Style; } set { SetValue(ItemContainerStyleProperty, value); } } /// /// Identifies the ItemContainerStyle dependency property. /// public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register( "ItemContainerStyle", typeof(Style), typeof(Accordion), new PropertyMetadata(null, OnItemContainerStylePropertyChanged)); /// /// ItemContainerStyleProperty property changed handler. /// /// /// TreeView that changed its ItemContainerStyle. /// /// Event arguments. private static void OnItemContainerStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Accordion source = (Accordion)d; Style value = e.NewValue as Style; source.ItemsControlHelper.UpdateItemContainerStyle(value); } #endregion public Style ItemContainerStyle #endif #region public Style AccordionButtonStyle /// /// Gets or sets the Style that is applied to AccordionButton elements /// in the AccordionItems. /// public Style AccordionButtonStyle { get { return GetValue(AccordionButtonStyleProperty) as Style; } set { SetValue(AccordionButtonStyleProperty, value); } } /// /// Identifies the AccordionButtonStyle dependency property. /// public static readonly DependencyProperty AccordionButtonStyleProperty = DependencyProperty.Register( AccordionButtonStyleName, typeof(Style), typeof(Accordion), new PropertyMetadata(null, OnAccordionButtonStylePropertyChanged)); /// /// AccordionButtonStyleProperty property changed handler. /// /// Accordion that changed its AccordionButtonStyle. /// Event arguments. private static void OnAccordionButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { } #endregion public Style AccordionButtonStyle #region public DataTemplate ContentTemplate /// /// Gets or sets the DataTemplate used to display the content /// of each generated AccordionItem. /// /// Either ContentTemplate or ItemTemplate is used. /// Setting both will result in an exception. public DataTemplate ContentTemplate { get { return (DataTemplate)GetValue(ContentTemplateProperty); } set { SetValue(ContentTemplateProperty, value); } } /// /// Identifies the ContentTemplate dependency property. /// public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register( "ContentTemplate", typeof(DataTemplate), typeof(Accordion), new PropertyMetadata(null)); #endregion public DataTemplate ContentTemplate #if SILVERLIGHT /// /// Occurs when the SelectedItem or SelectedItems property value changes. /// public event SelectionChangedEventHandler SelectionChanged; #else /// /// Occurs when the SelectedItem or SelectedItems property value changes. /// public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(Accordion)); /// /// Occurs when the SelectedItem or SelectedItems property value changes. /// public event SelectionChangedEventHandler SelectionChanged { add { base.AddHandler(SelectionChangedEvent, value); } remove { base.RemoveHandler(SelectionChangedEvent, value); } } #endif /// /// Occurs when the SelectedItems collection changes. /// public event NotifyCollectionChangedEventHandler SelectedItemsChanged; #if !SILVERLIGHT /// /// Static constructor /// static Accordion() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Accordion), new FrameworkPropertyMetadata(typeof(Accordion))); } #endif /// /// Initializes a new instance of the class. /// public Accordion() { #if SILVERLIGHT DefaultStyleKey = typeof(Accordion); #endif ItemsControlHelper = new ItemsControlHelper(this); ObservableCollection items = new ObservableCollection(); ObservableCollection indices = new ObservableCollection(); SelectedItems = items; SelectedIndices = indices; items.CollectionChanged += OnSelectedItemsCollectionChanged; indices.CollectionChanged += OnSelectedIndicesCollectionChanged; _scheduledActions = new List(); SizeChanged += OnAccordionSizeChanged; Interaction = new InteractionHelper(this); } /// /// Builds the visual tree for the Accordion control when a /// new template is applied. /// public override void OnApplyTemplate() { ItemsControlHelper.OnApplyTemplate(); base.OnApplyTemplate(); Interaction.OnApplyTemplateBase(); } /// /// Returns a AccordionAutomationPeer for use by the Silverlight /// automation infrastructure. /// /// A AccordionAutomationPeer object for the Accordion. protected override AutomationPeer OnCreateAutomationPeer() { return new AccordionAutomationPeer(this); } #region ItemsControl /// /// Creates or identifies the element that is used to display the given /// item. /// /// /// The element that is used to display the given item. /// protected override DependencyObject GetContainerForItemOverride() { return new AccordionItem(); } /// /// Determines if the specified item is (or is eligible to be) its own /// container. /// /// The item to check. /// /// True if the item is (or is eligible to be) its own container; /// otherwise, false. /// protected override bool IsItemItsOwnContainerOverride(object item) { return item is AccordionItem; } /// /// Prepares the specified element to display the specified item. /// /// The element used to display the specified item. /// The item to display. protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { AccordionItem accordionItem = element as AccordionItem; if (accordionItem != null) { DataTemplate specifiedContentTemplate = accordionItem.ContentTemplate; base.PrepareContainerForItemOverride(element, item); ItemsControlHelper.PrepareContainerForItemOverride(accordionItem, ItemContainerStyle); AccordionItem.PreparePrepareHeaderedContentControlContainerForItemOverride(accordionItem, item, this, ItemContainerStyle); // after base.prepare, item template has replaced contenttemplate DataTemplate displayMemberTemplate = accordionItem.ContentTemplate; // put original contenttemplate back that was overwritten // It takes precendence over a generated itemtemplate. // this might mean setting back a null, which is correct given the bindings // that follow. accordionItem.ContentTemplate = specifiedContentTemplate; // potentially set contenttemplate if accordionItem did not specify one explicitly if (accordionItem.ContentTemplate == null) { accordionItem.SetBinding( ContentControl.ContentTemplateProperty, new Binding("ContentTemplate") { Source = this, Mode = BindingMode.OneWay }); } // potentially set headertemplate if accordionItem did not specify one explicitly if (accordionItem.HeaderTemplate == null) { accordionItem.SetBinding( HeaderedContentControl.HeaderTemplateProperty, new Binding("ItemTemplate") { Source = this, Mode = BindingMode.OneWay }); } // potentially bind AccordionButtonStyle. if (accordionItem.AccordionButtonStyle == null) { accordionItem.SetBinding( AccordionItem.AccordionButtonStyleProperty, new Binding(AccordionButtonStyleName) { Source = this, Mode = BindingMode.OneWay }); } // possibly set a displaymemberPath on header or content. if (displayMemberTemplate != null && !string.IsNullOrEmpty(DisplayMemberPath)) { if (accordionItem.ContentTemplate == null) { accordionItem.ContentTemplate = displayMemberTemplate; } if (accordionItem.HeaderTemplate == null) { accordionItem.HeaderTemplate = displayMemberTemplate; } } // give accordionItem a reference back to the parent Accordion. accordionItem.ParentAccordion = this; // SelectedItem is expected to be set while adding items. // Check: does this item belong in the selectedindices int index = ItemContainerGenerator.IndexFromContainer(accordionItem); if (!accordionItem.IsSelected && SelectedIndices.Contains(index)) { accordionItem.IsSelected = true; } // could also be adding an item with the IsSelected set to true. if (accordionItem.IsSelected) { SelectedItem = item; } // item might have been preselected when added to the item collection. // at that point the parent had not been registered yet, so no notification was done. if (accordionItem.IsSelected) { if (!SelectedItems.OfType().Contains(item)) { SelectedItems.Add(item); } if (!SelectedIndices.Contains(index)) { SelectedIndices.Add(index); } } accordionItem.ExpandDirection = ExpandDirection; } else { base.PrepareContainerForItemOverride(element, item); ItemsControlHelper.PrepareContainerForItemOverride(element, ItemContainerStyle); } // The panel will register itself when it has had a child to add. SetPanelOrientation(); // change has occured, re-evaluate the locked status on items SetLockedProperties(); // At this moment this item has not been added to the panel yet, so we schedule a layoutpass Dispatcher.BeginInvoke(new Action(LayoutChildren)); } /// /// Undoes the effects of the /// method. /// /// The container element. /// The item that should be cleared. protected override void ClearContainerForItemOverride(DependencyObject element, object item) { AccordionItem accordionItem = element as AccordionItem; if (accordionItem != null) { accordionItem.IsLocked = false; accordionItem.IsSelected = false; // release the parent child relationship. accordionItem.ParentAccordion = null; } base.ClearContainerForItemOverride(element, item); } /// /// Invoked when the /// property changes. /// /// Information about the change. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { base.OnItemsChanged(e); switch (e.Action) { case NotifyCollectionChangedAction.Add: try { _isIgnoringSelectedIndicesChanges = true; for (int i = 0; i < SelectedIndices.Count; i++) { if (SelectedIndices[i] >= e.NewStartingIndex) { // add a value of one SelectedIndices[i] = SelectedIndices[i] + 1; } } } finally { _isIgnoringSelectedIndicesChanges = false; } if (SelectedIndex >= e.NewStartingIndex && SelectedIndex > -1) { SelectedIndex++; } // now add the item, will also add indice at correct position. if (SelectedItem == null && IsMinimumOneSelected) { if (!SelectedItems.OfType().Contains(e.NewItems[0])) { SelectedItems.Add(e.NewItems[0]); } SelectedItem = e.NewItems[0]; } break; case NotifyCollectionChangedAction.Reset: { _isIgnoringSelectedIndicesChanges = true; _isIgnoringSelectedItemsChanges = true; try { // Items has been cleared. // so clear selecteditems as well SelectedItems.Clear(); SelectedIndices.Clear(); SelectedItem = null; SelectedIndex = -1; } finally { _isIgnoringSelectedIndicesChanges = false; _isIgnoringSelectedItemsChanges = false; } // we receive this action when an itemssource is set InitializeNewItemsSource(); } break; case NotifyCollectionChangedAction.Remove: { int index = e.OldStartingIndex; object item = e.OldItems[0]; try { _isIgnoringSelectedIndicesChanges = true; if (SelectedIndices.Contains(index)) { SelectedIndices.Remove(index); } for (int i = 0; i < SelectedIndices.Count; i++) { if (SelectedIndices[i] > index) { // lower the value by one SelectedIndices[i] = SelectedIndices[i] - 1; } } } finally { _isIgnoringSelectedIndicesChanges = false; } try { _isIgnoringSelectedItemsChanges = true; if (SelectedItems.Contains(item)) { // check that there are no indices pointing to similar // items that are still in the collection if (SelectedIndices.Count(i => i < Items.Count && Items[i].Equals(item)) == 0) { SelectedItems.Remove(item); } } } finally { _isIgnoringSelectedItemsChanges = false; } if (SelectedIndex == index) { // that item is no longer in the Items collection // so the index is incorrect as well SelectedIndex = -1; } if (SelectedIndex > e.OldStartingIndex && SelectedIndex > -1) { SelectedIndex -= 1; } } break; } SetPanelOrientation(); } /// /// Initializes the SelectedItem property when a new ItemsSource is set. /// private void InitializeNewItemsSource() { // todo: remove the SelectedItem == null check (should not be necessary) // possibly an ItemsSource has been set if (IsMinimumOneSelected && SelectedItem == null && Items.Count > 0) { if (!SelectedItems.OfType().Contains(Items[0])) { SelectedItems.Add(Items[0]); } SelectedItem = Items[0]; } } #endregion ItemsControl #region Selection handling /// /// Called when an AccordionItem is unselected. /// /// The accordion item that was unselected. internal void OnAccordionItemUnselected(AccordionItem accordionItem) { UnselectItem(ItemContainerGenerator.IndexFromContainer(accordionItem), ItemContainerGenerator.ItemFromContainer(accordionItem)); } /// /// Unselects the item. /// /// The index of the item that will be unselected. /// The item that will be unselected. Can be null. private void UnselectItem(int index, object item) { if (index < 0 || index > Items.Count) { // invalid return; } // try through accordionitem AccordionItem container = index >= 0 && index < Items.Count ? ItemContainerGenerator.ContainerFromIndex(index) as AccordionItem : null; if (container != null && container.IsSelected) { container.IsLocked = false; container.IsSelected = false; return; } item = item ?? Items[index]; int newSelectedIndex = -1; // shortcuts to new item selection. if (SelectedIndex > -1 && SelectedIndex == index) { // this item is no longer the selected item. // in order to keep the amount of raised events down, will select a new selected item here. newSelectedIndex = ProposeSelectedIndexCandidate(index); // no cancelling possible, undo the action. // current template makes sure accordionheader does not allow this unselect // that behavior is not enforced which means that it could come // from a SelectedItems/Indices manipulation SelectedIndex = newSelectedIndex; } // update selecteditems collection if (SelectedItems.OfType().Contains(item) && index != newSelectedIndex && !item.Equals(SelectedItem)) { // if there are indices still pointing to a similar item, do not remove if (SelectedIndices.Count(i => i != index && i < Items.Count && Items[i].Equals(item)) == 0) { if (_isInSelectedItemsCollectionChanged) { throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } SelectedItems.Remove(item); } } // indexes should always be changed, even if we are reselecting a similar item if (SelectedIndices.Contains(index)) { if (_isInSelectedIndicesCollectionChanged) { throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } SelectedIndices.Remove(index); } } /// /// Called when an AccordionItem selected. /// /// The accordion item that was selected. internal void OnAccordionItemSelected(AccordionItem accordionItem) { SelectItem(ItemContainerGenerator.IndexFromContainer(accordionItem)); } /// /// Selects the item. /// /// The index of the item to select. private void SelectItem(int index) { // try through accordionitem AccordionItem container = index >= 0 && index < Items.Count ? ItemContainerGenerator.ContainerFromIndex(index) as AccordionItem : null; if (container != null && !container.IsSelected) { container.IsSelected = true; return; } SelectedIndex = index; object item = Items[index]; if (item != null) { // update selecteditems collection if (!SelectedItems.OfType().Contains(item)) { if (_isInSelectedItemsCollectionChanged) { throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } SelectedItems.Add(item); } if (!SelectedIndices.Contains(index)) { if (_isInSelectedIndicesCollectionChanged) { throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } SelectedIndices.Add(index); } } } /// /// Changes the selected item, by unselecting and selecting where /// necessary. /// /// The old index. /// The new index. private void ChangeSelectedIndex(int oldIndex, int newIndex) { AccordionItem oldAccordionItem = oldIndex >= 0 && oldIndex < Items.Count ? ItemContainerGenerator.ContainerFromIndex(oldIndex) as AccordionItem : null; AccordionItem newAccordionItem = newIndex >= 0 && newIndex < Items.Count ? ItemContainerGenerator.ContainerFromIndex(newIndex) as AccordionItem : null; // unselect the previous item, if we need to // we should be able to be called when the oldvalue equals the newvalue if (oldIndex != newIndex) { // we only need to explicitly deselect the oldvalue if there is a maximum // of one selected. However, if user explicitly set SelectedItem // to null, we should still deselect the old value. if (IsMaximumOneSelected || newIndex == -1) { if (oldAccordionItem != null) { // unselection can be triggered by selection of another item. oldAccordionItem.IsLocked = false; oldAccordionItem.IsSelected = false; } else if (oldIndex > -1) { // there was no wrapper yet, fallback to regular unselecting UnselectItem(oldIndex, null); } #region raise event for UIAutomation. if (newAccordionItem != null && AutomationPeer.ListenerExists( AutomationEvents.SelectionItemPatternOnElementSelected)) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(newAccordionItem); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected); } } #endregion } } // make the selection through the container if possible if (newAccordionItem != null) { newAccordionItem.IsSelected = true; } else if (newIndex != -1) { // there was no wrapper yet, fallback to regular selecting SelectItem(newIndex); } SelectedIndex = newIndex; } /// /// Called when selected items collection changed. /// /// The sender. /// The /// instance containing the event data. private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (_isIgnoringSelectedItemsChanges) { return; } _isInSelectedItemsCollectionChanged = true; Action unselectItem = item => { // since we removed this selecteditem, all selectedindices need to be removed as well List valid = SelectedIndices.Where(i => i < Items.Count && item.Equals(Items[i])).ToList(); if (valid.Count > 0) { foreach (int index in valid) { UnselectItem(index, item); } } else { UnselectItem(Items.IndexOf(item), item); } }; switch (e.Action) { case NotifyCollectionChangedAction.Add: { if (IsMaximumOneSelected && (SelectedItem != null && !e.NewItems.Contains(SelectedItem))) { // will always lead to manipulation of the collection throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } foreach (object item in e.NewItems) { object tempItem = item; SelectedItem = tempItem; } } break; case NotifyCollectionChangedAction.Remove: { if (IsMinimumOneSelected && e.OldItems.Contains(SelectedItem)) { // will always lead to manipulation of the collection throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } foreach (object item in e.OldItems) { object tempItem = item; unselectItem(tempItem); } } break; case NotifyCollectionChangedAction.Reset: { // unselect all items. // we use the selectedindices collection to pinpoint the // items we need to unselect if (IsMinimumOneSelected && Items.Count > 0) { // will always lead to manipulation of the collection throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } for (int i = SelectedIndices.Count - 1; i >= 0; i--) { int selectedIndex = SelectedIndices[i]; if (selectedIndex < Items.Count) { object tempItem = Items[selectedIndex]; unselectItem(tempItem); } } } break; default: { string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.Accordion_UnsupportedCollectionAction, e.Action); throw new NotSupportedException(message); } } // let the outside world know RaiseOnSelectedItemsCollectionChanged(e); _isInSelectedItemsCollectionChanged = false; } /// /// Called when selected indices collection changed. /// /// The sender. /// The /// instance containing the event data. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Method is best kept coherent.")] private void OnSelectedIndicesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (_isIgnoringSelectedIndicesChanges) { return; } _isInSelectedIndicesCollectionChanged = true; switch (e.Action) { case NotifyCollectionChangedAction.Add: { if (IsMaximumOneSelected) { // selectedindex always trails the actual state if (SelectedItem != null && e.NewItems.Count != 1 || ((int)e.NewItems[0] < Items.Count && !Items[(int)e.NewItems[0]].Equals(SelectedItem))) { // will always lead to manipulation of the collection throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } } foreach (int index in e.NewItems) { if (index < Items.Count) { SelectedIndex = index; // raise event for UIAutomation, which uses SelectedIndices to query SelectedItems. if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection)) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(this); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementAddedToSelection); } } } } } break; case NotifyCollectionChangedAction.Remove: { if (IsMinimumOneSelected && e.OldItems.Contains(SelectedIndex)) { if (SelectedIndex < Items.Count && Items[SelectedIndex].Equals(SelectedItem) && SelectedIndices.Count == 0) { // will always lead to manipulation of the collection throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } } foreach (int index in e.OldItems) { if (index < Items.Count) { UnselectItem(index, null); // raise event for UIAutomation, which uses SelectedIndices to query SelectedItems. if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(this); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection); } } } } } break; case NotifyCollectionChangedAction.Reset: { // unselect all items. // we use the selectedindices collection to pinpoint the // items we need to unselect if (IsMinimumOneSelected && Items.Count > 0) { // will always lead to manipulation of the collection throw new InvalidOperationException(Properties.Resources.Accordion_InvalidManipulationOfSelectionCollections); } // unselect all items. // we use the selectedItems collection to pinpoint the // items we need to unselect for (int i = SelectedItems.Count - 1; i >= 0; i--) { object item = SelectedItems[i]; UnselectItem(i, item); // raise event for UIAutomation, which uses SelectedIndices to query SelectedItems. if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) { AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(this); if (peer != null) { peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection); } } } } break; default: { string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.Accordion_UnsupportedCollectionAction, e.Action); throw new NotSupportedException(message); } } // change has occured, re-evaluate the locked status on items SetLockedProperties(); // do a layout pass. LayoutChildren(); _isInSelectedIndicesCollectionChanged = false; } #region Helpers /// /// Gets an item that is suitable for selection. /// /// Index that should not be considered if /// possible. /// An item that should be selected. This could be nonCandidateIndex, /// if no other possibility was found. private int ProposeSelectedIndexCandidate(int nonCandidateIndex) { // other non candidates are items that are exactly like this item. object item = (nonCandidateIndex >= 0 && nonCandidateIndex < Items.Count) ? Items[nonCandidateIndex] : null; // see if we can find a suitable item in the selecteditems collection IEnumerable validIndices = SelectedIndices.Where(i => i != nonCandidateIndex && (item == null || !item.Equals(Items[i]))); if (validIndices.Count() > 0) { return validIndices.First(); } if (IsMinimumOneSelected && Items.Count > 0) { return 0; } return -1; } /// /// Selects all the AccordionItems in the Accordion control. /// /// If the Accordion SelectionMode is OneOrMore or ZeroOrMore all /// AccordionItems would be selected. If the Accordion SelectionMode is /// One or ZeroOrOne all items would be selected and unselected. Only /// the last AccordionItem would remain selected. public void SelectAll() { UpdateAccordionItemsSelection(true); } /// /// Unselects all the AccordionItems in the Accordion control. /// /// If the Accordion SelectionMode is Zero or ZeroOrMore all /// AccordionItems would be Unselected. If SelectionMode is One or /// OneOrMode than all items would be Unselected and selected. Only the /// first AccordionItem would still be selected. public void UnselectAll() { UpdateAccordionItemsSelection(false); } /// /// Updates all accordionItems to be selected or unselected. /// /// True to select all items, false to unselect. /// Will not attempt to change a locked accordionItem. private void UpdateAccordionItemsSelection(bool selectedValue) { foreach (object item in Items) { AccordionItem accordionItem = ItemContainerGenerator.ContainerFromItem(item) as AccordionItem; if (accordionItem != null && !accordionItem.IsLocked) { accordionItem.IsSelected = selectedValue; } } } /// /// Sets the locked properties on all the items. /// private void SetLockedProperties() { // an item that can not be unselected is locked. // This happens in 'One' or 'OneOrMore' selection mode, when the first item is selected. for (int i = 0; i < Items.Count; i++) { AccordionItem item = ItemContainerGenerator.ContainerFromIndex(i) as AccordionItem; if (item != null) { item.IsLocked = (item.IsSelected && IsMinimumOneSelected && SelectedIndices.Count == 1); } } } /// /// Raises the SelectedItemChanged event when the SelectedItem /// property value changes. /// /// The /// instance containing the event data. protected virtual void OnSelectedItemChanged(SelectionChangedEventArgs e) { #if SILVERLIGHT SelectionChangedEventHandler handler = SelectionChanged; if (handler != null) { handler(this, e); } #else RaiseEvent(e); #endif } /// /// Raise the SelectedItemsCollectionChanged event. /// /// The /// instance containing the event data. /// This event is raised after the changes to the collection /// have been processed. private void RaiseOnSelectedItemsCollectionChanged(NotifyCollectionChangedEventArgs e) { NotifyCollectionChangedEventHandler handler = SelectedItemsChanged; if (handler != null) { handler(this, e); } } #endregion #endregion Selection handling #region Layout /// /// Called when the size of the Accordion changes. /// /// The sender. /// The /// instance containing the event data. private void OnAccordionSizeChanged(object sender, SizeChangedEventArgs e) { IsResizing = true; LayoutChildren(); IsResizing = false; } /// /// Called when size of a Header on the item changes. /// /// The item whose Header changed. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "item", Justification = "Passing the AccordionItem leads to a better API if we wish to change modifier to protected in the future.")] internal void OnHeaderSizeChange(AccordionItem item) { LayoutChildren(); } /// /// Allows an AccordionItem to signal the need for a visual action /// (resize, collapse, expand). /// /// The AccordionItem that signals for a schedule. /// The action it is scheduling for. /// True if the item is allowed to proceed without scheduling, /// false if the item needs to wait for a signal to execute the action. internal virtual bool ScheduleAction(AccordionItem item, AccordionAction action) { if (SelectionSequence == SelectionSequence.CollapseBeforeExpand) { lock (this) { if (!_scheduledActions.Contains(item)) { _scheduledActions.Add(item); } } if (_currentActioningItem == null) { Dispatcher.BeginInvoke(new Action(StartNextAction)); } return false; } else { return true; } } /// /// Signals the finish of an action by an item. /// /// The AccordionItem that finishes an action. /// An AccordionItem should always signal a finish, for this call /// will start the next scheduled action. internal virtual void OnActionFinish(AccordionItem item) { if (SelectionSequence == SelectionSequence.CollapseBeforeExpand) { lock (this) { if (!_currentActioningItem.Equals(item)) { throw new InvalidOperationException(Properties.Resources.Accordion_OnActionFinish_InvalidFinish); } _currentActioningItem = null; StartNextAction(); } } } /// /// Starts the next action in the list, in a particular order. /// /// An AccordionItem is should always signal that it is /// finished with an action. private void StartNextAction() { if (_currentActioningItem != null) { return; } // First do collapses, then resizes and finally expands. AccordionItem next = _scheduledActions.FirstOrDefault(item => item.ScheduledAction == AccordionAction.Collapse); if (next == null) { next = _scheduledActions.FirstOrDefault(item => item.ScheduledAction == AccordionAction.Resize); } if (next == null) { next = _scheduledActions.FirstOrDefault(item => item.ScheduledAction == AccordionAction.Expand); } if (next != null) { _currentActioningItem = next; _scheduledActions.Remove(next); next.StartAction(); } } /// /// Determines and sets the height of the accordion items. /// private void LayoutChildren() { ScrollViewer root = ItemsControlHelper.ScrollHost; Size targetSize = new Size(double.NaN, double.NaN); if (root != null && ItemsControlHelper.ItemsHost != null) { if (IsShouldFillWidth) { // selected items should fill the remaining width of the container. targetSize.Width = Math.Max(0, root.ViewportWidth - ItemsControlHelper.ItemsHost.ActualWidth); // calculate space currently occupied by items. This space will be redistributed. foreach (object item in Items) { AccordionItem accordionItem = ItemContainerGenerator.ContainerFromItem(item) as AccordionItem; if (accordionItem != null) { targetSize.Width += accordionItem.RelevantContentSize.Width; } } // offset for the real difference in viewportheight and actualheight. This happens when accordion // was made smaller. double smaller = root.ViewportWidth - ItemsControlHelper.ItemsHost.ActualWidth; if (smaller < 0) { targetSize.Width = Math.Max(0, targetSize.Width + smaller); } // calculated the targetsize for all selected items. Because of rounding issues, the // actual space taken sometimes exceeds the appropriate amount by a fraction. if (targetSize.Width > 1) { targetSize.Width -= 1; } // possibly we are bigger than we would want, the items // are overflowing. Always try to fit in current viewport. if (root.ExtentWidth > root.ViewportWidth) { targetSize.Width = Math.Max(0, targetSize.Width - (root.ExtentWidth - root.ViewportWidth)); } // calculate targetsize per selected item. This is redistribution. targetSize.Width = SelectedItems.Count > 0 ? targetSize.Width / SelectedItems.Count : targetSize.Width; } else if (IsShouldFillHeight) { // selected items should fill the remaining width of the container. targetSize.Height = Math.Max(0, root.ViewportHeight - ItemsControlHelper.ItemsHost.ActualHeight); // calculate space currently occupied by items. This space will be redistributed. foreach (object item in Items) { AccordionItem accordionItem = ItemContainerGenerator.ContainerFromItem(item) as AccordionItem; if (accordionItem != null) { targetSize.Height += accordionItem.RelevantContentSize.Height; } } // offset for the real difference in viewportheight and actualheight. This happens when accordion // was made smaller. double smaller = root.ViewportHeight - ItemsControlHelper.ItemsHost.ActualHeight; if (smaller < 0) { targetSize.Height = Math.Max(0, targetSize.Height + smaller); } // calculated the targetsize for all selected items. Because of rounding issues, the // actual space taken sometimes exceeds the appropriate amount by a fraction. if (targetSize.Height > 1) { targetSize.Height -= 1; } // calculate targetsize per selected item. This is redistribution. targetSize.Height = SelectedItems.Count > 0 ? targetSize.Height / SelectedItems.Count : targetSize.Height; } // set that targetsize foreach (object item in Items) { AccordionItem accordionItem = ItemContainerGenerator.ContainerFromItem(item) as AccordionItem; if (accordionItem != null) { // the calculated target size is calculated for the selected items. if (accordionItem.IsSelected) { accordionItem.ContentTargetSize = targetSize; } } } } } /// /// Gets a value indicating whether the accordion fills width. /// private bool IsShouldFillWidth { get { return (ExpandDirection == ExpandDirection.Left || ExpandDirection == ExpandDirection.Right) && (!Double.IsNaN(Width) || HorizontalAlignment == HorizontalAlignment.Stretch); } } /// /// Gets a value indicating whether the accordion fills height. /// private bool IsShouldFillHeight { get { return (ExpandDirection == ExpandDirection.Down || ExpandDirection == ExpandDirection.Up) && (!Double.IsNaN(Height) || VerticalAlignment == VerticalAlignment.Stretch); } } /// /// Sets the orientation of the panel. /// private void SetPanelOrientation() { StackPanel panel = ItemsControlHelper.ItemsHost as StackPanel; if (panel != null) { switch (ExpandDirection) { case ExpandDirection.Down: case ExpandDirection.Up: panel.HorizontalAlignment = HorizontalAlignment.Stretch; panel.VerticalAlignment = ExpandDirection == ExpandDirection.Down ? VerticalAlignment.Top : VerticalAlignment.Bottom; panel.Orientation = Orientation.Vertical; break; case ExpandDirection.Left: case ExpandDirection.Right: panel.VerticalAlignment = VerticalAlignment.Stretch; panel.HorizontalAlignment = ExpandDirection == ExpandDirection.Left ? HorizontalAlignment.Right : HorizontalAlignment.Left; panel.Orientation = Orientation.Horizontal; break; } } } #endregion Layout #region Visual state management /// /// Provides handling for the GotFocus event. /// /// The data for the event. protected override void OnGotFocus(RoutedEventArgs e) { if (Interaction.AllowGotFocus(e)) { Interaction.OnGotFocusBase(); base.OnGotFocus(e); } } /// /// Provides handling for the LostFocus event. /// /// The data for the event. protected override void OnLostFocus(RoutedEventArgs e) { if (Interaction.AllowLostFocus(e)) { Interaction.OnLostFocusBase(); base.OnLostFocus(e); } } /// /// Provides handling for the MouseEnter event. /// /// The data for the event. protected override void OnMouseEnter(MouseEventArgs e) { if (Interaction.AllowMouseEnter(e)) { Interaction.OnMouseEnterBase(); base.OnMouseEnter(e); } } /// /// Provides handling for the MouseLeave event. /// /// The data for the event. protected override void OnMouseLeave(MouseEventArgs e) { if (Interaction.AllowMouseLeave(e)) { Interaction.OnMouseLeaveBase(); base.OnMouseLeave(e); } } /// /// Provides handling for the MouseLeftButtonDown event. /// /// The data for the event. protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (Interaction.AllowMouseLeftButtonDown(e)) { Interaction.OnMouseLeftButtonDownBase(); base.OnMouseLeftButtonDown(e); } } /// /// Called before the MouseLeftButtonUp event occurs. /// /// The data for the event. protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { if (Interaction.AllowMouseLeftButtonUp(e)) { Interaction.OnMouseLeftButtonUpBase(); base.OnMouseLeftButtonUp(e); } } /// /// Update the visual state of the control. /// /// /// A value indicating whether to automatically generate transitions to /// the new state, or instantly transition to the new state. /// void IUpdateVisualState.UpdateVisualState(bool useTransitions) { UpdateVisualState(useTransitions); } /// /// Update the current visual state of the button. /// /// /// True to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// internal virtual void UpdateVisualState(bool useTransitions) { // Handle the Common and Focused states Interaction.UpdateVisualStateBase(useTransitions); } #endregion Visual state management } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/AccordionAction.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// Determines the action the AccordionItem will perform. /// /// Preview internal enum AccordionAction { /// /// No action will be performed. /// None, /// /// A collapse will be performed. /// Collapse, /// /// An expand will be performed. /// Expand, /// /// A resize will be performed. /// Resize } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/AccordionButton.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls.Primitives { /// /// Represents the header for an accordion item. /// /// By creating a seperate control, there is more flexibility in /// the templating possibilities. /// Preview [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateExpanded, GroupName = VisualStates.GroupExpansion)] [TemplateVisualState(Name = VisualStates.StateCollapsed, GroupName = VisualStates.GroupExpansion)] [TemplateVisualState(Name = VisualStates.StateExpandDown, GroupName = VisualStates.GroupExpandDirection)] [TemplateVisualState(Name = VisualStates.StateExpandUp, GroupName = VisualStates.GroupExpandDirection)] [TemplateVisualState(Name = VisualStates.StateExpandLeft, GroupName = VisualStates.GroupExpandDirection)] [TemplateVisualState(Name = VisualStates.StateExpandRight, GroupName = VisualStates.GroupExpandDirection)] public class AccordionButton : ToggleButton { #region Parent AccordionItem /// /// Gets or sets a reference to the parent AccordionItem /// of an AccordionButton. /// /// The parent accordion item. internal AccordionItem ParentAccordionItem { get; set; } #endregion Parent AccordionItem #if !SILVERLIGHT /// /// Static constructor /// static AccordionButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AccordionButton), new FrameworkPropertyMetadata(typeof(AccordionButton))); } #endif /// /// Initializes a new instance of the /// class. /// public AccordionButton() { #if SILVERLIGHT DefaultStyleKey = typeof(AccordionButton); #endif } /// /// Updates the state of the visual. /// /// If set to true use transitions. /// The header will follow the parent accordionitem states. internal virtual void UpdateVisualState(bool useTransitions) { // the visualstate of the header is completely dependent on the parent state. if (ParentAccordionItem == null) { return; } if (ParentAccordionItem.IsSelected) { VisualStates.GoToState(this, useTransitions, VisualStates.StateExpanded); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateCollapsed); } switch (ParentAccordionItem.ExpandDirection) { // no animations on an expanddirection change. case ExpandDirection.Down: VisualStates.GoToState(this, false, VisualStates.StateExpandDown); break; case ExpandDirection.Up: VisualStates.GoToState(this, false, VisualStates.StateExpandUp); break; case ExpandDirection.Left: VisualStates.GoToState(this, false, VisualStates.StateExpandLeft); break; default: VisualStates.GoToState(this, false, VisualStates.StateExpandRight); break; } } } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/AccordionItem.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Globalization; using System.Windows.Automation.Peers; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls { /// /// Represents a control that displays a header and has a collapsible /// content window. /// /// Preview [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)] [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] [TemplateVisualState(Name = VisualStates.StateExpanded, GroupName = VisualStates.GroupExpansion)] [TemplateVisualState(Name = VisualStates.StateCollapsed, GroupName = VisualStates.GroupExpansion)] [TemplateVisualState(Name = VisualStates.StateLocked, GroupName = VisualStates.GroupLocked)] [TemplateVisualState(Name = VisualStates.StateUnlocked, GroupName = VisualStates.GroupLocked)] [TemplateVisualState(Name = VisualStates.StateExpandDown, GroupName = VisualStates.GroupExpandDirection)] [TemplateVisualState(Name = VisualStates.StateExpandUp, GroupName = VisualStates.GroupExpandDirection)] [TemplateVisualState(Name = VisualStates.StateExpandLeft, GroupName = VisualStates.GroupExpandDirection)] [TemplateVisualState(Name = VisualStates.StateExpandRight, GroupName = VisualStates.GroupExpandDirection)] [TemplatePart(Name = ElementExpandSiteName, Type = typeof(ExpandableContentControl))] [TemplatePart(Name = ElementExpanderButtonName, Type = typeof(AccordionButton))] [StyleTypedProperty(Property = "AccordionButtonStyle", StyleTargetType = typeof(AccordionButton))] [StyleTypedProperty(Property = "ExpandableContentControlStyle", StyleTargetType = typeof(ExpandableContentControl))] public class AccordionItem : HeaderedContentControl, IUpdateVisualState { #region Template Parts /// /// The name of the ExpanderButton template part. /// private const string ElementExpanderButtonName = "ExpanderButton"; /// /// The name of the ExpandSite template part. /// private const string ElementExpandSiteName = "ExpandSite"; /// /// The ExpanderButton template part is a templated ToggleButton that's /// used to select and unselect this AccordionItem. /// private AccordionButton _expanderButton; /// /// Gets or sets the ExpanderButton template part. /// private AccordionButton ExpanderButton { get { return _expanderButton; } set { // Detach from old ExpanderButton if (_expanderButton != null) { _expanderButton.Click -= OnExpanderButtonClicked; _expanderButton.ParentAccordionItem = null; _expanderButton.SizeChanged -= OnHeaderSizeChanged; } _expanderButton = value; if (_expanderButton != null) { _expanderButton.IsChecked = IsSelected; _expanderButton.Click += OnExpanderButtonClicked; _expanderButton.ParentAccordionItem = this; _expanderButton.SizeChanged += OnHeaderSizeChanged; } } } /// /// BackingField for the ExpandSite property. /// private ExpandableContentControl _expandSite; /// /// Gets or sets the expand site template part. /// private ExpandableContentControl ExpandSite { get { return _expandSite; } set { _expandSite = value; } } #endregion #region public ExpandDirection ExpandDirection /// /// Determines whether the ExpandDirection property may be written. /// private bool _allowedToWriteExpandDirection; /// /// Gets the direction in which the AccordionItem content window opens. /// public ExpandDirection ExpandDirection { get { return (ExpandDirection)GetValue(ExpandDirectionProperty); } protected internal set { _allowedToWriteExpandDirection = true; SetValue(ExpandDirectionProperty, value); _allowedToWriteExpandDirection = false; } } /// /// Identifies the ExpandDirection dependency property. /// public static readonly DependencyProperty ExpandDirectionProperty = DependencyProperty.Register( "ExpandDirection", typeof(ExpandDirection), typeof(AccordionItem), new PropertyMetadata(ExpandDirection.Down, OnExpandDirectionPropertyChanged)); /// /// ExpandDirectionProperty PropertyChangedCallback call back static /// function. /// This function validates the new value before calling virtual function /// OnExpandDirectionChanged. /// /// Expander object whose ExpandDirection property is /// changed. /// DependencyPropertyChangedEventArgs which contains /// the old and new values. private static void OnExpandDirectionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AccordionItem ctrl = (AccordionItem)d; ExpandDirection oldValue = (ExpandDirection)e.OldValue; ExpandDirection newValue = (ExpandDirection)e.NewValue; if (!ctrl._allowedToWriteExpandDirection) { // revert to old value ctrl.ExpandDirection = oldValue; string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.AccordionItem_InvalidWriteToExpandDirection, newValue); throw new InvalidOperationException(message); } // invalid value. This check is not of great importance anymore since // the previous check should catch all invalid sets. if (newValue != ExpandDirection.Down && newValue != ExpandDirection.Left && newValue != ExpandDirection.Right && newValue != ExpandDirection.Up) { // revert to old value ctrl.ExpandDirection = oldValue; string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.Expander_OnExpandDirectionPropertyChanged_InvalidValue, newValue); throw new ArgumentException(message, "e"); } if (ctrl.ExpandSite != null) { // Jump to correct percentage after a direction change #if SILVERLIGHT ctrl.ExpandSite.Percentage = ctrl.IsSelected ? 1 : 0; #else // ctrl.ExpandSite.RecalculatePercentage(ctrl.IsSelected ? 1 : 0); #endif } ctrl.UpdateVisualState(true); } #endregion public ExpandDirection ExpandDirection #region public bool IsSelected /// /// Gets or sets a value indicating whether the AccordionItem is /// selected and its content window is visible. /// public bool IsSelected { get { return (bool)GetValue(IsSelectedProperty); } set { SetValue(IsSelectedProperty, value); } } /// /// Identifies the IsSelected dependency property. /// public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register( "IsSelected", typeof(bool), typeof(AccordionItem), new PropertyMetadata(OnIsSelectedPropertyChanged)); /// /// SelectedProperty PropertyChangedCallback static function. /// /// Expander object whose Expanded property is changed. /// DependencyPropertyChangedEventArgs which contains the /// old and new values. private static void OnIsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AccordionItem ctrl = (AccordionItem) d; bool isSelected = (bool) e.NewValue; // Not allowed to change the IsSelected state when locked. if (ctrl.IsLocked && ctrl._isSelectedNestedLevel == 0) { ctrl._isSelectedNestedLevel++; ctrl.SetValue(IsSelectedProperty, e.OldValue); ctrl._isSelectedNestedLevel--; throw new InvalidOperationException(Properties.Resources.AccordionItem_OnIsSelectedPropertyChanged_InvalidChange); } if (ctrl._isSelectedNestedLevel == 0) { Accordion parent = ctrl.ParentAccordion; if (parent != null) { if (isSelected) { parent.OnAccordionItemSelected(ctrl); } else { parent.OnAccordionItemUnselected(ctrl); } } if (isSelected) { ctrl.OnSelected(); } else { ctrl.OnUnselected(); } } } /// /// Nested level for IsSelectedCoercion. /// private int _isSelectedNestedLevel; #endregion public bool IsSelected #region public bool IsLocked /// /// Gets a value indicating whether the AccordionItem cannot be /// selected by the user. /// /// True if this instance is locked; otherwise, false. /// The IsSelected property may not be changed when the /// AccordionItem is locked. Locking occurs when the item is the first /// in the list, the SelectionMode of Accordion requires atleast one selected /// AccordionItem and the AccordionItem is currently selected. public bool IsLocked { get { return _isLocked; } internal set { if (_isLocked != value) { _isLocked = value; UpdateVisualState(true); } } } /// /// BackingField for IsLocked. /// private bool _isLocked; #endregion public bool IsLocked #region public Style AccordionButtonStyle /// /// Gets or sets the Style used by AccordionButton. /// public Style AccordionButtonStyle { get { return GetValue(AccordionButtonStyleProperty) as Style; } set { SetValue(AccordionButtonStyleProperty, value); } } /// /// Identifies the AccordionButtonStyle dependency property. /// public static readonly DependencyProperty AccordionButtonStyleProperty = DependencyProperty.Register( "AccordionButtonStyle", typeof(Style), typeof(AccordionItem), new PropertyMetadata(OnAccordionButtonStylePropertyChanged)); /// /// AccordionButtonStyleProperty property changed handler. /// /// AccordionItem that changed its AccordionButtonStyle. /// Event arguments. private static void OnAccordionButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AccordionItem source = (AccordionItem) d; source.OnAccordionButtonStyleChanged(e.OldValue as Style, e.NewValue as Style); } /// /// Called when AccordionButtonStyle is changed. /// /// The old style. /// The new style. protected virtual void OnAccordionButtonStyleChanged(Style oldStyle, Style newStyle) { } #endregion public Style AccordionButtonStyle #region public Style ExpandableContentControlStyle /// /// Gets or sets the Style used by ExpandableContentControl. /// public Style ExpandableContentControlStyle { get { return GetValue(ExpandableContentControlStyleProperty) as Style; } set { SetValue(ExpandableContentControlStyleProperty, value); } } /// /// Identifies the ExpandableContentControlStyle dependency property. /// public static readonly DependencyProperty ExpandableContentControlStyleProperty = DependencyProperty.Register( "ExpandableContentControlStyle", typeof(Style), typeof(AccordionItem), new PropertyMetadata(OnExpandableContentControlStylePropertyChanged)); /// /// ExpandableContentControlStyleProperty property changed handler. /// /// AccordionItem that changed its ExpandableContentControlStyle. /// Event arguments. private static void OnExpandableContentControlStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AccordionItem source = (AccordionItem)d; source.OnExpandableContentControlStyleChanged(e.OldValue as Style, e.NewValue as Style); } /// /// Called when ExpandableContentControlStyle is changed. /// /// The old style. /// The new style. protected virtual void OnExpandableContentControlStyleChanged(Style oldStyle, Style newStyle) { } #endregion public Style ExpandableContentControlStyle #region public Size ContentTargetSize /// /// Determines whether it is allowed to set the ContentTargetSize /// property. /// private bool _allowedToWriteContentTargetSize; /// /// Gets the Size that the content will animate to. /// public Size ContentTargetSize { get { return (Size)GetValue(ContentTargetSizeProperty); } internal set { _allowedToWriteContentTargetSize = true; SetValue(ContentTargetSizeProperty, value); _allowedToWriteContentTargetSize = false; } } /// /// Identifies the ContentTargetSize dependency property. /// public static readonly DependencyProperty ContentTargetSizeProperty = DependencyProperty.Register( "ContentTargetSize", typeof(Size), typeof(AccordionItem), new PropertyMetadata(new Size(double.NaN, double.NaN), OnContentTargetSizePropertyChanged)); /// /// ContentTargetSizeProperty property changed handler. /// /// AccordionItem that changed its ContentTargetSize. /// Event arguments. private static void OnContentTargetSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AccordionItem source = (AccordionItem)d; Size targetSize = (Size)e.NewValue; if (!source._allowedToWriteContentTargetSize) { // revert to old value source.ContentTargetSize = (Size)e.OldValue; throw new InvalidOperationException(Properties.Resources.AccordionItem_InvalidWriteToContentTargetSize); } } #endregion public Size ContentTargetSize #region Accordion ParentAccordion /// /// Gets or sets a reference to the parent Accordion of an /// AccordionItem. /// internal Accordion ParentAccordion { get; set; } #endregion Accordion ParentAccordion /// /// Gets the scheduled action. /// /// The scheduled action. internal AccordionAction ScheduledAction { get; private set; } /// /// Occurs when the accordionItem is selected. /// public static RoutedEvent SelectedEvent = EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AccordionItem)); /// /// Occurs when the accordionItem is selected. /// public event RoutedEventHandler Selected { add { AddHandler(SelectedEvent, value); } remove { RemoveHandler(SelectedEvent, value); } } /// /// Occurs when the accordionItem is unselected. /// public static RoutedEvent UnselectedEvent = EventManager.RegisterRoutedEvent("Unselected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AccordionItem)); /// /// Occurs when the accordionItem is unselected. /// public event RoutedEventHandler Unselected { add { AddHandler(UnselectedEvent, value); } remove { RemoveHandler(UnselectedEvent, value); } } /// /// Static constructor /// static AccordionItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AccordionItem), new FrameworkPropertyMetadata(typeof(AccordionItem))); } /// /// Initializes a new instance of the AccordionItem class. /// public AccordionItem() { // initialize to no action. ScheduledAction = AccordionAction.None; #if SILVERLIGHT DefaultStyleKey = typeof(AccordionItem); #endif _interaction = new InteractionHelper(this); } #region Layout /// /// Called when the size of the control changes. /// /// The sender. /// The /// instance containing the event data. private void OnHeaderSizeChanged(object sender, SizeChangedEventArgs e) { // allow the parent to reschedule a layout pass. if (ParentAccordion != null) { ParentAccordion.OnHeaderSizeChange(this); } } /// /// Gets the relevant size of the current content. /// internal Size RelevantContentSize { get { return ExpandSite == null ? new Size(0, 0) : ExpandSite.RelevantContentSize; } } /// /// Schedules the specified action. /// /// The action to be performed. private void Schedule(AccordionAction action) { #if SILVERLIGHT if (DesignerProperties.GetIsInDesignMode(this) && ExpandSite != null) { switch (action) { case AccordionAction.None: break; case AccordionAction.Collapse: ExpandSite.Percentage = 0; break; case AccordionAction.Expand: case AccordionAction.Resize: ExpandSite.Percentage = 1; break; default: throw new ArgumentOutOfRangeException("action"); } return; } #endif ScheduledAction = action; if (ParentAccordion == null) { // no parentaccordion to notify, so just execute. StartAction(); } else { bool directExecute = ParentAccordion.ScheduleAction(this, action); if (directExecute) { StartAction(); } } } /// /// Starts an action, such as resize, collapse or expand. /// internal virtual void StartAction() { if (ScheduledAction == AccordionAction.None) { throw new InvalidOperationException(Properties.Resources.AccordionItem_StartAction_InvalidCall); } Action layoutAction; switch (ScheduledAction) { case AccordionAction.Collapse: layoutAction = () => { VisualStateManager.GoToState(this, VisualStates.StateExpanded, false); // We only want to notify the parent that this action is finished when we are done with // the state transition. In SL this is done through OnStoryboardFinished, but the same code // in WPF would result in a corrupted state because OnStoryboardFinished gets called more frequently than expected. // In the WPF case, we make the scheduled action synchornous to ensure the transition is properly done. #if SILVERLIGHT VisualStateManager.GoToState(this, VisualStates.StateCollapsed, true); #else if (VisualStateManager.GoToState(this, VisualStates.StateCollapsed, true)) { ParentAccordion.OnActionFinish(this); } #endif }; break; case AccordionAction.Expand: layoutAction = () => { VisualStateManager.GoToState(this, VisualStates.StateCollapsed, false); #if SILVERLIGHT VisualStateManager.GoToState(this, VisualStates.StateExpanded, true); #else if (VisualStateManager.GoToState(this, VisualStates.StateExpanded, true)) { ParentAccordion.OnActionFinish(this); } #endif }; break; case AccordionAction.Resize: layoutAction = () => { // trigger ExpandedState to run again, by quickly moving to collapsed. // the effect is not noticeable because no layout pass is done. VisualStateManager.GoToState(this, VisualStates.StateExpanded, false); VisualStateManager.GoToState(this, VisualStates.StateCollapsed, false); #if SILVERLIGHT VisualStateManager.GoToState(this, VisualStates.StateExpanded, true); #else if (VisualStateManager.GoToState(this, VisualStates.StateExpanded, true)) { ParentAccordion.OnActionFinish(this); } #endif }; break; default: { string message = string.Format( CultureInfo.InvariantCulture, Properties.Resources.AccordionItem_StartAction_InvalidAction, ScheduledAction); throw new NotSupportedException(message); } } ScheduledAction = AccordionAction.None; layoutAction(); } #if SILVERLIGHT /// /// Called when a storyboard finishes. /// /// The AccordionItem that finished a storyboard. /// The instance containing /// the event data. /// AccordionItem is required to make this call. private void OnStoryboardFinished(object sender, EventArgs e) { _isBusyWithAction = false; if (ParentAccordion != null) { ParentAccordion.OnActionFinish(this); } } #endif /// /// Gets a value indicating whether the AccordionItem fills width. /// private bool ShouldFillWidth { get { return (ExpandDirection == ExpandDirection.Left || ExpandDirection == ExpandDirection.Right) && (!Double.IsNaN(ContentTargetSize.Width) || HorizontalAlignment == HorizontalAlignment.Stretch); } } /// /// Gets a value indicating whether the AccordionItem fills height. /// private bool ShouldFillHeight { get { return (ExpandDirection == ExpandDirection.Down || ExpandDirection == ExpandDirection.Up) && (!Double.IsNaN(ContentTargetSize.Height) || VerticalAlignment == VerticalAlignment.Stretch); } } #endregion Layout /// /// Builds the visual tree for the AccordionItem control when a new /// template is applied. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); ExpanderButton = GetTemplateChild(ElementExpanderButtonName) as AccordionButton; ExpandSite = GetTemplateChild(ElementExpandSiteName) as ExpandableContentControl; if (VisualTreeHelper.GetChildrenCount(this) > 0) { FrameworkElement root = VisualTreeHelper.GetChild(this, 0) as FrameworkElement; #if SILVERLIGHT if (root != null) { ExpandStoryboard = (from stategroup in (VisualStateManager.GetVisualStateGroups(root) as Collection) where stategroup.Name == VisualStates.GroupExpansion from state in (stategroup.States as Collection) where state.Name == VisualStates.StateExpanded select state.Storyboard).FirstOrDefault(); CollapseStoryboard = (from stategroup in (VisualStateManager.GetVisualStateGroups(root) as Collection) where stategroup.Name == VisualStates.GroupExpansion from state in (stategroup.States as Collection) where state.Name == VisualStates.StateCollapsed select state.Storyboard).FirstOrDefault(); } else { ExpandStoryboard = null; CollapseStoryboard = null; } #endif } _interaction.OnApplyTemplateBase(); UpdateVisualState(false); // the UpdateVisualState will not set the expand or collapse state. if (IsSelected) { Schedule(AccordionAction.Expand); } else { Schedule(AccordionAction.Collapse); } } /// /// Returns a AccordionItemAutomationPeer for use by the Silverlight /// automation infrastructure. /// /// A AccordionItemAutomationPeer object for the AccordionItem. protected override AutomationPeer OnCreateAutomationPeer() { #if SILVERLIGHT return new AccordionItemAutomationPeer(this); #else return new AccordionItemWrapperAutomationPeer(this); #endif } /// /// Prepares the specified container to display the specified item. /// /// /// Container element used to display the specified item. /// /// Specified item to display. /// The parent ItemsControl. /// /// The ItemContainerStyle for the parent ItemsControl. /// [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "parent", Justification = "Following ItemsControl signature.")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "parentItemContainerStyle", Justification = "Following ItemsControl signature.")] internal static void PreparePrepareHeaderedContentControlContainerForItemOverride(HeaderedContentControl element, object item, ItemsControl parent, Style parentItemContainerStyle) { if (element != item) { // We do not have proper access to Visual. // Nor do we keep track of the HeaderIsItem property. if (!(item is UIElement) && HasDefaultValue(element, HeaderProperty)) { element.Header = item; } } } /// /// Check whether a control has the default value for a property. /// /// The control to check. /// The property to check. /// /// True if the property has the default value; false otherwise. /// private static bool HasDefaultValue(Control control, DependencyProperty property) { Debug.Assert(control != null, "control should not be null!"); Debug.Assert(property != null, "property should not be null!"); return control.ReadLocalValue(property) == DependencyProperty.UnsetValue; } /// /// Provides handling for the KeyDown event. /// /// Key event args. protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.Handled || !IsEnabled || IsLocked) { return; } bool isSelected = IsSelected; switch (ExpandDirection) { case ExpandDirection.Down: if ((isSelected && e.Key == Key.Up) || (!isSelected && e.Key == Key.Down)) { IsSelected = !isSelected; } break; case ExpandDirection.Up: if ((isSelected && e.Key == Key.Down) || (!isSelected && e.Key == Key.Up)) { IsSelected = !isSelected; } break; case ExpandDirection.Left: if ((isSelected && e.Key == Key.Right) || (!isSelected && e.Key == Key.Left)) { IsSelected = !isSelected; } break; case ExpandDirection.Right: if ((isSelected && e.Key == Key.Left) || (!isSelected && e.Key == Key.Right)) { IsSelected = !isSelected; } break; } } /// /// Raises the Selected event when the IsSelected property changes /// from false to true. /// protected virtual void OnSelected() { #if SILVERLIGHT ToggleSelected(Selected, new RoutedEventArgs()); #else ToggleSelected(new RoutedEventArgs(SelectedEvent)); #endif } /// /// Raises the Unselected event when the IsSelected property changes /// from true to false. /// protected virtual void OnUnselected() { #if SILVERLIGHT ToggleSelected(Unselected, new RoutedEventArgs()); #else ToggleSelected(new RoutedEventArgs(UnselectedEvent)); #endif } #if SILVERLIGHT /// /// Handle changes to the IsSelected property. /// /// Event handler. /// Event arguments. private void ToggleSelected(RoutedEventHandler handler, RoutedEventArgs args) { #else /// /// Handle changes to the IsSelected property. /// /// Event arguments. private void ToggleSelected(RoutedEventArgs args) { #endif ToggleButton expander = ExpanderButton; if (expander != null) { expander.IsChecked = IsSelected; } if (IsSelected) { Schedule(AccordionAction.Expand); } else { Schedule(AccordionAction.Collapse); } UpdateVisualState(true); #if SILVERLIGHT RaiseEvent(handler, args); #else // WPF has a slightly different mechanism for raising routed events RaiseEvent(args); #endif } #if SILVERLIGHT /// /// Raise a RoutedEvent. /// /// Event handler. /// Event arguments. private void RaiseEvent(RoutedEventHandler handler, RoutedEventArgs args) { if (handler != null) { handler(this, args); } } #endif /// /// Handle ExpanderButton's click event. /// /// The ExpanderButton in template. /// Routed event arg. private void OnExpanderButtonClicked(object sender, RoutedEventArgs e) { // If the item is locked, the item is not permitted to change its selection state if (!IsLocked) { IsSelected = !IsSelected; } } #region Visual state management /// /// Gets or sets the helper that provides all of the standard /// interaction functionality. /// private InteractionHelper _interaction; /// /// Provides handling for the GotFocus event. /// /// The data for the event. protected override void OnGotFocus(RoutedEventArgs e) { if (_interaction.AllowGotFocus(e)) { _interaction.OnGotFocusBase(); base.OnGotFocus(e); } } /// /// Provides handling for the LostFocus event. /// /// The data for the event. protected override void OnLostFocus(RoutedEventArgs e) { if (_interaction.AllowLostFocus(e)) { _interaction.OnLostFocusBase(); base.OnLostFocus(e); } } /// /// Provides handling for the MouseEnter event. /// /// The data for the event. protected override void OnMouseEnter(MouseEventArgs e) { if (_interaction.AllowMouseEnter(e)) { _interaction.OnMouseEnterBase(); base.OnMouseEnter(e); } } /// /// Provides handling for the MouseLeave event. /// /// The data for the event. protected override void OnMouseLeave(MouseEventArgs e) { if (_interaction.AllowMouseLeave(e)) { _interaction.OnMouseLeaveBase(); base.OnMouseLeave(e); } } /// /// Provides handling for the MouseLeftButtonDown event. /// /// The data for the event. protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (_interaction.AllowMouseLeftButtonDown(e)) { _interaction.OnMouseLeftButtonDownBase(); base.OnMouseLeftButtonDown(e); } } /// /// Called before the MouseLeftButtonUp event occurs. /// /// The data for the event. protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { if (_interaction.AllowMouseLeftButtonUp(e)) { _interaction.OnMouseLeftButtonUpBase(); base.OnMouseLeftButtonUp(e); } } /// /// Update the visual state of the control. /// /// /// A value indicating whether to automatically generate transitions to /// the new state, or instantly transition to the new state. /// void IUpdateVisualState.UpdateVisualState(bool useTransitions) { UpdateVisualState(useTransitions); } /// /// Update the current visual state of the button. /// /// /// True to use transitions when updating the visual state, false to /// snap directly to the new visual state. /// internal virtual void UpdateVisualState(bool useTransitions) { if (IsLocked) { VisualStates.GoToState(this, useTransitions, VisualStates.StateLocked); } else { VisualStates.GoToState(this, useTransitions, VisualStates.StateUnlocked); } switch (ExpandDirection) { case ExpandDirection.Down: VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandDown); break; case ExpandDirection.Up: VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandUp); break; case ExpandDirection.Left: VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandLeft); break; default: VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandRight); break; } // let the header know a change has possibly occured. if (ExpanderButton != null) { ExpanderButton.UpdateVisualState(useTransitions); } // Handle the Common and Focused states _interaction.UpdateVisualStateBase(useTransitions); } #endregion } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/AccordionSelectionMode.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// Defines the minimum and maximum number of selected items allowed in an Accordion control. /// /// Preview public enum AccordionSelectionMode { /// /// Exactly one item must be selected in the Accordion. /// One, /// /// At least one item must be selected in the Accordion. /// OneOrMore, /// /// No more than one item can be selected in the accordion. /// ZeroOrOne, /// /// Any number of items can be selected in the Accordion. /// ZeroOrMore } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/ExpandableContentControl.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls.Primitives { public class ExpandableContentControl : ContentControl { static ExpandableContentControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpandableContentControl), new FrameworkPropertyMetadata(typeof(ExpandableContentControl))); ClipToBoundsProperty.OverrideMetadata(typeof(ExpandableContentControl), new FrameworkPropertyMetadata(true)); FocusableProperty.OverrideMetadata(typeof(ExpandableContentControl), new FrameworkPropertyMetadata(false)); } #region public ExpandDirection RevealMode /// /// Gets or sets the direction in which the ExpandableContentControl /// content window opens. /// public ExpandDirection RevealMode { get { return (ExpandDirection) GetValue(RevealModeProperty); } set { SetValue(RevealModeProperty, value); } } /// /// Identifies the RevealMode dependency property. /// public static readonly DependencyProperty RevealModeProperty = DependencyProperty.Register("RevealMode", typeof(ExpandDirection), typeof(ExpandableContentControl), new FrameworkPropertyMetadata(ExpandDirection.Down, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange), IsRevealModeValid); private static bool IsRevealModeValid(object value) { return value != null && Enum.IsDefined(typeof(ExpandDirection), value); } /// /// Gets a value indicating whether the content should be revealed horizontally. /// private bool IsHorizontalRevealMode { get { return RevealMode == ExpandDirection.Left || RevealMode == ExpandDirection.Right; } } /// /// Gets a value indicating whether the content should be revealed verticaly. /// private bool IsVerticalRevealMode { get { return RevealMode == ExpandDirection.Up || RevealMode == ExpandDirection.Down; } } #endregion public ExpandDirection RevealMode #region public double Percentage /// /// Gets or sets the relative percentage of the content that is /// currently visible. A percentage of 1 corresponds to the complete /// TargetSize. /// public double Percentage { get { return (double) GetValue(PercentageProperty); } set { SetValue(PercentageProperty, value); } } /// /// Identifies the Percentage dependency property. /// public static readonly DependencyProperty PercentageProperty = DependencyProperty.Register("Percentage", typeof(double), typeof(ExpandableContentControl), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange)); #endregion public double Percentage #region IsExpanded public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(ExpandableContentControl), new PropertyMetadata(false)); public bool IsExpanded { get { return (bool) GetValue(IsExpandedProperty); } set { SetValue(IsExpandedProperty, value); } } #endregion private UIElement Child { get { return VisualChildrenCount == 0 ? null : GetVisualChild(0) as UIElement; } } /// /// Gets the content current visible size. /// internal Size RelevantContentSize { get { return new Size( (IsHorizontalRevealMode ? Width : 0), (IsVerticalRevealMode ? Height : 0)); } } protected override Size MeasureOverride(Size availableSize) { var child = Child; var size = new Size(); if (child != null) { child.Measure(availableSize); size = child.DesiredSize; } return Resize(size, Percentage); } private Size Resize(Size size, double factor) { return IsHorizontalRevealMode ? new Size(size.Width*factor, size.Height) : new Size(size.Width, size.Height*factor); } protected override Size ArrangeOverride(Size finalSize) { var child = Child; if (child == null || Math.Abs(Percentage) < 0.0001) return finalSize; var rect = new Rect(Resize(finalSize, 1/Percentage)); if (RevealMode == ExpandDirection.Down) rect.Y = -rect.Height*(1 - Percentage); else if (RevealMode == ExpandDirection.Left) rect.X = -rect.Width*(1 - Percentage); child.Arrange(rect); return finalSize; } } } ================================================ FILE: WpfToolkit/Layout/Accordion/System/Windows/Controls/SelectionSequence.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. namespace System.Windows.Controls { /// /// Determines the order in which visual states are set. /// /// Preview public enum SelectionSequence { /// /// Collapses are set before expansions. /// CollapseBeforeExpand, /// /// No delays, all states are set immediately. /// Simultaneous } } ================================================ FILE: WpfToolkit/Layout/DotNetProjects.Layout.Toolkit.csproj ================================================  System.Windows.Controls net5.0-windows;net6.0-windows;net4 true WPF Toolkit Layout true DotNetProjects.snk true DotNetProjects.WpfToolkit.Layout DotNetProjects DotNetProjects DotNetProjects.WPF Toolkit 2021 DotNetProjects 1.0.0 1.0.0.0 1.0.0.0 MS-PL Extensions\Extensions.cs Common\InteractionHelper.cs Common\ItemsControlHelper.cs Common\IUpdateVisualState.cs Common\VisualStates.cs Common\VisualTreeExtensions.cs Common\WeakEventListener.cs ================================================ FILE: WpfToolkit/Layout/Extensions/MathExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace System.Windows.Controls.Extensions { /// Provides extension methods for trigonometric, logarithmic, and other common mathematical functions. internal static class MathExtensions { #region Approximation /// Returns the smallest integer greater than or equal to the specified decimal number. /// The smallest integer greater than or equal to value. /// A decimal number. public static decimal Ceiling(this decimal value) { return Math.Ceiling(value); } /// Returns the smallest integer greater than or equal to the specified double-precision floating-point number. /// The smallest integer greater than or equal to value. If value is equal to , , or , that value is returned. /// A double-precision floating-point number. public static double Ceiling(this double value) { return Math.Ceiling(value); } /// Returns the largest integer less than or equal to the specified decimal number. /// The largest integer less than or equal to value. /// A decimal number. public static decimal Floor(this decimal value) { return Math.Floor(value); } /// Returns the largest integer less than or equal to the specified double-precision floating-point number. /// The largest integer less than or equal to value. If value is equal to , , or , that value is returned. /// A double-precision floating-point number. public static double Floor(this double value) { return Math.Floor(value); } /// Returns the larger of two 8-bit unsigned integers. /// Parameter a or b, whichever is larger. /// The first of two 8-bit unsigned integers to compare. /// The second of two 8-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static byte AtLeast(this byte a, byte b) { return Math.Max(a, b); } /// Returns the larger of two decimal numbers. /// Parameter a or b, whichever is larger. /// The first of two numbers to compare. /// The second of two numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static decimal AtLeast(this decimal a, decimal b) { return Math.Max(a, b); } /// Returns the larger of two double-precision floating-point numbers. /// Parameter a or b, whichever is larger. If a, b, or both a and b are equal to , is returned. /// The first of two double-precision floating-point numbers to compare. /// The second of two double-precision floating-point numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double AtLeast(this double a, double b) { return Math.Max(a, b); } /// Returns the larger of two single-precision floating-point numbers. /// Parameter a or b, whichever is larger. If a, or b, or both a and b are equal to , is returned. /// The first of two single-precision floating-point numbers to compare. /// The second of two single-precision floating-point numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static float AtLeast(this float a, float b) { return Math.Max(a, b); } /// Returns the larger of two 16-bit signed integers. /// Parameter a or b, whichever is larger. /// The first of two 16-bit signed integers to compare. /// The second of two 16-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static short AtLeast(this short a, short b) { return Math.Max(a, b); } /// Returns the larger of two 32-bit signed integers. /// Parameter a or b, whichever is larger. /// The first of two 32-bit signed integers to compare. /// The second of two 32-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static int AtLeast(this int a, int b) { return Math.Max(a, b); } /// Returns the larger of two 64-bit signed integers. /// Parameter a or b, whichever is larger. /// The first of two 64-bit signed integers to compare. /// The second of two 64-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static long AtLeast(this long a, long b) { return Math.Max(a, b); } /// Returns the larger of two 8-bit signed integers. /// Parameter a or b, whichever is larger. /// The first of two 8-bit unsigned integers to compare. /// The second of two 8-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static sbyte AtLeast(this sbyte a, sbyte b) { return Math.Max(a, b); } /// Returns the larger of two 16-bit unsigned integers. /// Parameter a or b, whichever is larger. /// The first of two 16-bit unsigned integers to compare. /// The second of two 16-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static ushort AtLeast(this ushort a, ushort b) { return Math.Max(a, b); } /// Returns the larger of two 32-bit unsigned integers. /// Parameter a or b, whichever is larger. /// The first of two 32-bit unsigned integers to compare. /// The second of two 32-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static uint AtLeast(this uint a, uint b) { return Math.Max(a, b); } /// Returns the larger of two 64-bit unsigned integers. /// Parameter a or b, whichever is larger. /// The first of two 64-bit unsigned integers to compare. /// The second of two 64-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static ulong AtLeast(this ulong a, ulong b) { return Math.Max(a, b); } /// Returns the smaller of two 8-bit unsigned integers. /// Parameter a or b, whichever is smaller. /// The first of two 8-bit unsigned integers to compare. /// The second of two 8-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static byte AtMost(this byte a, byte b) { return Math.Min(a, b); } /// Returns the smaller of two decimal numbers. /// Parameter a or b, whichever is smaller. /// The first of two numbers to compare. /// The second of two numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static decimal AtMost(this decimal a, decimal b) { return Math.Min(a, b); } /// Returns the smaller of two double-precision floating-point numbers. /// Parameter a or b, whichever is smaller. If a, b, or both a and b are equal to , is returned. /// The first of two double-precision floating-point numbers to compare. /// The second of two double-precision floating-point numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double AtMost(this double a, double b) { return Math.Min(a, b); } /// Returns the smaller of two single-precision floating-point numbers. /// Parameter a or b, whichever is smaller. If a, b, or both a and b are equal to , is returned. /// The first of two single-precision floating-point numbers to compare. /// The second of two single-precision floating-point numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static float AtMost(this float a, float b) { return Math.Min(a, b); } /// Returns the smaller of two 16-bit signed integers. /// Parameter a or b, whichever is smaller. /// The first of two 16-bit signed integers to compare. /// The second of two 16-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static short AtMost(this short a, short b) { return Math.Min(a, b); } /// Returns the smaller of two 32-bit signed integers. /// Parameter a or b, whichever is smaller. /// The first of two 32-bit signed integers to compare. /// The second of two 32-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static int AtMost(this int a, int b) { return Math.Min(a, b); } /// Returns the smaller of two 64-bit signed integers. /// Parameter a or b, whichever is smaller. /// The first of two 64-bit signed integers to compare. /// The second of two 64-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static long AtMost(this long a, long b) { return Math.Min(a, b); } /// Returns the smaller of two 8-bit signed integers. /// Parameter a or b, whichever is smaller. /// The first of two 8-bit signed integers to compare. /// The second of two 8-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static sbyte AtMost(this sbyte a, sbyte b) { return Math.Min(a, b); } /// Returns the smaller of two 16-bit unsigned integers. /// Parameter a or b, whichever is smaller. /// The first of two 16-bit unsigned integers to compare. /// The second of two 16-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static ushort AtMost(this ushort a, ushort b) { return Math.Min(a, b); } /// Returns the smaller of two 32-bit unsigned integers. /// Parameter a or b, whichever is smaller. /// The first of two 32-bit unsigned integers to compare. /// The second of two 32-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static uint AtMost(this uint a, uint b) { return Math.Min(a, b); } /// Returns the smaller of two 64-bit unsigned integers. /// Parameter a or b, whichever is smaller. /// The first of two 64-bit unsigned integers to compare. /// The second of two 64-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static ulong AtMost(this ulong a, ulong b) { return Math.Min(a, b); } /// Returns the value constrained inclusively between two 8-bit unsigned integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 8-bit unsigned integers to compare. /// The second of two 8-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static byte Clamp(this byte value, byte a, byte b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two decimal numbers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two numbers to compare. /// The second of two numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static decimal Clamp(this decimal value, decimal a, decimal b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two double-precision floating-point numbers. /// A value between a and b inclusively. If a, b, or both a and b are equal to , is returned. /// The value to restrict between a and b. /// The first of two double-precision floating-point numbers to compare. /// The second of two double-precision floating-point numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Clamp(this double value, double a, double b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two single-precision floating-point numbers. /// A value between a and b inclusively. If a, b, or both a and b are equal to , is returned. /// The value to restrict between a and b. /// The first of two single-precision floating-point numbers to compare. /// The second of two single-precision floating-point numbers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static float Clamp(this float value, float a, float b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 16-bit signed integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 16-bit signed integers to compare. /// The second of two 16-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static short Clamp(this short value, short a, short b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 32-bit signed integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 32-bit signed integers to compare. /// The second of two 32-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static int Clamp(this int value, int a, int b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 64-bit signed integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 64-bit signed integers to compare. /// The second of two 64-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static long Clamp(this long value, long a, long b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 8-bit signed integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 8-bit signed integers to compare. /// The second of two 8-bit signed integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static sbyte Clamp(this sbyte value, sbyte a, sbyte b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 16-bit unsigned integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 16-bit unsigned integers to compare. /// The second of two 16-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static ushort Clamp(this ushort value, ushort a, ushort b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 32-bit unsigned integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 32-bit unsigned integers to compare. /// The second of two 32-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static uint Clamp(this uint value, uint a, uint b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Returns the value constrained inclusively between two 64-bit unsigned integers. /// A value between a and b inclusively. /// The value to restrict between a and b. /// The first of two 64-bit unsigned integers to compare. /// The second of two 64-bit unsigned integers to compare. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static ulong Clamp(this ulong value, ulong a, ulong b) { return a < b ? Math.Min(Math.Max(value, a), b) : Math.Max(Math.Min(value, a), b); } /// Rounds a decimal value to the nearest integer. /// The integer nearest parameter value. If value is halfway between two integers, one of which is even and the other odd, then the even number is returned. /// A decimal number to be rounded. /// The result is outside the range of a . public static decimal Round(this decimal value) { return Math.Round(value); } /// Rounds a double-precision floating-point value to the nearest integer. /// The integer nearest value. If value is halfway between two integers, one of which is even and the other odd, then the even number is returned. /// A double-precision floating-point number to be rounded. public static double Round(this double value) { return Math.Round(value); } /// Rounds a decimal value to a specified precision. /// The number nearest value with a precision equal to decimals. If value is halfway between two numbers, one of which is even and the other odd, then the even number is returned. If the precision of value is less than decimals, then value is returned unchanged. /// A decimal number to be rounded. /// The number of significant decimal places (precision) in the return value. /// The result is outside the range of a . /// decimals is less than 0 or greater than 28. public static decimal Round(this decimal value, int decimals) { return Math.Round(value, decimals); } /// Rounds a decimal value to the nearest integer. A parameter specifies how to round the value if it is midway between two other numbers. /// The integer nearest value. If value is halfway between two numbers, one of which is even and the other odd, then mode determines which of the two is returned. /// A decimal number to be rounded. /// Specification for how to round value if it is midway between two other numbers. /// The result is outside the range of a . /// mode is not a valid value of . public static decimal Round(this decimal value, MidpointRounding mode) { return Math.Round(value, mode); } /// Rounds a double-precision floating-point value to the specified precision. /// The number nearest value with a precision equal to digits. If value is halfway between two numbers, one of which is even and the other odd, then the even number is returned. If the precision of value is less than digits, then value is returned unchanged. /// The number of significant digits (precision) in the return value. /// A double-precision floating-point number to be rounded. /// digits is less than 0 or greater than 15. public static double Round(this double value, int digits) { return Math.Round(value, digits); } /// Rounds a double-precision floating-point value to the nearest integer. A parameter specifies how to round the value if it is midway between two other numbers. /// The integer nearest value. If value is halfway between two integers, one of which is even and the other odd, then mode determines which of the two is returned. /// Specification for how to round value if it is midway between two other numbers. /// A double-precision floating-point number to be rounded. /// mode is not a valid value of . public static double Round(this double value, MidpointRounding mode) { return Math.Round(value, mode); } /// Rounds a decimal value to a specified precision. A parameter specifies how to round the value if it is midway between two other numbers. /// The number nearest value with a precision equal to decimals. If value is halfway between two numbers, one of which is even and the other odd, then mode determines which of the two numbers is returned. If the precision of value is less than decimals, then value is returned unchanged. /// A decimal number to be rounded. /// Specification for how to round value if it is midway between two other numbers. /// The number of significant decimal places (precision) in the return value. /// The result is outside the range of a . /// decimals is less than 0 or greater than 28. /// mode is not a valid value of . public static decimal Round(this decimal value, int decimals, MidpointRounding mode) { return Math.Round(value, decimals, mode); } /// Rounds a double-precision floating-point value to the specified precision. A parameter specifies how to round the value if it is midway between two other numbers. /// The number nearest value with a precision equal to digits. If value is halfway between two numbers, one of which is even and the other odd, then the mode parameter determines which number is returned. If the precision of value is less than digits, then value is returned unchanged. /// Specification for how to round value if it is midway between two other numbers. /// The number of significant digits (precision) in the return value. /// A double-precision floating-point number to be rounded. /// digits is less than 0 or greater than 15. /// mode is not a valid value of . public static double Round(this double value, int digits, MidpointRounding mode) { return Math.Round(value, digits, mode); } /// Rounds a decimal value to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A decimal number to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static decimal RoundUp(this decimal value, decimal factor) { var d = (value % factor); return d == 0 ? value : value < 0 ? value - d : value + (factor - d); } /// Rounds a double-precision floating-point value to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A double-precision floating-point number to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static double RoundUp(this double value, double factor) { var d = (value % factor); return d == 0 ? value : value < 0 ? value - d : value + (factor - d); } /// Rounds a single-precision floating-point value to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A single-precision floating-point number to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static float RoundUp(this float value, float factor) { var d = (value % factor); return d == 0 ? value : value < 0 ? value - d : value + (factor - d); } /// Rounds a 64-bit signed integer to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A 64-bit signed integer to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static long RoundUp(this long value, long factor) { var d = (value % factor); return d == 0 ? value : value < 0 ? value - d : value + (factor - d); } /// Rounds a 32-bit signed integer to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A 32-bit signed integer to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static int RoundUp(this int value, int factor) { var d = (value % factor); return d == 0 ? value : value < 0 ? value - d : value + (factor - d); } /// Rounds a 16-bit signed integer to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A 16-bit signed integer to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static short RoundUp(this short value, short factor) { var d = (value % factor); return (short)(d == 0 ? value : value < 0 ? value - d : value + (factor - d)); } /// Rounds a 8-bit signed integer to the next multiple of the specified factor. /// The nearest multiple of factor that is greater than or equal to value. /// A 8-bit signed integer to be rounded. /// The factor to round the value to. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static byte RoundUp(this byte value, byte factor) { var d = (value % factor); return (byte)(d == 0 ? value : value + (factor - d)); } /// Rounds a decimal value to the next integer. /// The nearest integer that is greater than or equal to value. /// A decimal number to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static decimal RoundUp(this decimal value) { return RoundUp(value, 1); } /// Rounds a double-precision floating-point value to the next integer. /// The nearest integer that is greater than or equal to value. /// A double-precision floating-point number to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static double RoundUp(this double value) { return RoundUp(value, 1); } /// Rounds a single-precision floating-point value to the next integer. /// The nearest integer that is greater than or equal to value. /// A single-precision floating-point number to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static float RoundUp(this float value) { return RoundUp(value, 1); } /// Rounds a 64-bit signed integer to the next integer. /// The nearest integer that is greater than or equal to value. /// A 64-bit signed integer to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static long RoundUp(this long value) { return RoundUp(value, 1); } /// Rounds a 32-bit signed integer to the next integer. /// The nearest integer that is greater than or equal to value. /// A 32-bit signed integer to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static int RoundUp(this int value) { return RoundUp(value, 1); } /// Rounds a 16-bit signed integer to the next integer. /// The nearest integer that is greater than or equal to value. /// A 16-bit signed integer to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static short RoundUp(this short value) { return RoundUp(value, (short)1); } /// Rounds a 8-bit signed integer to the next integer. /// The nearest integer that is greater than or equal to value. /// A 8-bit signed integer to be rounded. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] public static byte RoundUp(this byte value) { return RoundUp(value, (byte)1); } /// Rounds a decimal value to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// A decimal number to be rounded. /// The factor to round the value to. public static decimal RoundDown(this decimal value, decimal factor) { var d = (value % factor); return d == 0 ? value : value > 0 ? value - d : value - (factor + d); } /// Rounds a double-precision floating-point value to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// A double-precision floating-point number to be rounded. /// The factor to round the value to. public static double RoundDown(this double value, double factor) { var d = (value % factor); return d == 0 ? value : value > 0 ? value - d : value - (factor + d); } /// Rounds a single-precision floating-point value to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// A single-precision floating-point number to be rounded. /// The factor to round the value to. public static float RoundDown(this float value, float factor) { var d = (value % factor); return d == 0 ? value : value > 0 ? value - d : value - (factor + d); } /// Rounds a 64-bit signed integer to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// A 64-bit signed integer to be rounded. /// The factor to round the value to. public static long RoundDown(this long value, long factor) { var d = (value % factor); return d == 0 ? value : value > 0 ? value - d : value - (factor + d); } /// Rounds a 32-bit signed integer to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// A 32-bit signed integer to be rounded. /// The factor to round the value to. public static int RoundDown(this int value, int factor) { var d = (value % factor); return d == 0 ? value : value > 0 ? value - d : value - (factor + d); } /// Rounds a 16-bit signed integer to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// A 16-bit signed integer to be rounded. /// The factor to round the value to. public static short RoundDown(this short value, short factor) { var d = (value % factor); return (short)(d == 0 ? value : value > 0 ? value - d : value - (factor + d)); } /// Rounds an 8-bit unsigned integer to the previous multiple of the specified factor. /// The nearest multiple of factor that is less than or equal to value. /// An 8-bit unsigned integer to be rounded. /// The factor to round the value to. public static byte RoundDown(this byte value, byte factor) { var d = (value % factor); return (byte)(value - d); } /// Rounds a decimal value to the previous integer. /// The nearest integer that is less than or equal to value. /// A decimal number to be rounded. public static decimal RoundDown(this decimal value) { return RoundDown(value, 1); } /// Rounds a double-precision floating-point value to the previous integer. /// The nearest integer that is less than or equal to value. /// A double-precision floating-point number to be rounded. public static double RoundDown(this double value) { return RoundDown(value, 1); } /// Rounds a single-precision floating-point value to the previous integer. /// The nearest integer that is less than or equal to value. /// A single-precision floating-point number to be rounded. public static float RoundDown(this float value) { return RoundDown(value, 1); } /// Rounds a 64-bit signed integer to the previous integer. /// The nearest integer that is less than or equal to value. /// A 64-bit signed integer to be rounded. public static long RoundDown(this long value) { return RoundDown(value, 1); } /// Rounds a 32-bit signed integer to the previous integer. /// The nearest integer that is less than or equal to value. /// A 32-bit signed integer to be rounded. public static int RoundDown(this int value) { return RoundDown(value, 1); } /// Rounds a 16-bit signed integer to the previous integer. /// The nearest integer that is less than or equal to value. /// A 16-bit signed integer to be rounded. public static short RoundDown(this short value) { return RoundDown(value, (short)1); } /// Rounds an 8-bit unsigned integer to the previous integer. /// The nearest integer that is less than or equal to value. /// An 8-bit unsigned integer to be rounded. public static byte RoundDown(this byte value) { return RoundDown(value, (byte)1); } /// Calculates the integral part of a specified decimal number. /// The integral part of value; that is, the number that remains after any fractional digits have been discarded. /// A number to truncate. public static decimal Truncate(this decimal value) { return Math.Truncate(value); } /// Calculates the integral part of a specified double-precision floating-point number. /// The integral part of value; that is, the number that remains after any fractional digits have been discarded. /// A number to truncate. public static double Truncate(this double value) { return Math.Truncate(value); } #endregion #region Arithmetic /// Returns the absolute value of a number. /// A , x, such that 0 ≤ x ≤ . /// A number in the range ≤ value ≤ . public static decimal Abs(this decimal value) { return Math.Abs(value); } /// Returns the absolute value of a double-precision floating-point number. /// A double-precision floating-point number, x, such that 0 ≤ x ≤ . /// A number in the range ≤ value ≤ . public static double Abs(this double value) { return Math.Abs(value); } /// Returns the absolute value of a single-precision floating-point number. /// A single-precision floating-point number, x, such that 0 ≤ x ≤ . /// A number in the range ≤ value ≤ . public static float Abs(this float value) { return Math.Abs(value); } /// Returns the absolute value of a 16-bit signed integer. /// A 16-bit signed integer, x, such that 0 ≤ x ≤ . /// A number in the range < value ≤ . /// value equals . public static short Abs(this short value) { return Math.Abs(value); } /// Returns the absolute value of a 32-bit signed integer. /// A 32-bit signed integer, x, such that 0 ≤ x ≤ . /// A number in the range < value ≤ . /// value equals . public static int Abs(this int value) { return Math.Abs(value); } /// Returns the absolute value of a 64-bit signed integer. /// A 64-bit signed integer, x, such that 0 ≤ x ≤ . /// A number in the range < value ≤ . /// value equals . public static long Abs(this long value) { return Math.Abs(value); } /// Returns the absolute value of an 8-bit signed integer. /// An 8-bit signed integer, x, such that 0 ≤ x ≤ . /// A number in the range < value ≤ . /// value equals . public static sbyte Abs(this sbyte value) { return Math.Abs(value); } /// Produces the full product of two 32-bit numbers. /// The containing the product of the specified numbers. /// The first to multiply. /// The second to multiply. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static long BigMul(this int a, int b) { return Math.BigMul(a, b); } /// Calculates the quotient of two 32-bit signed integers and also returns the remainder in an output parameter. /// The containing the quotient of the specified numbers. /// The that contains the dividend. /// The that receives the remainder. /// The that contains the divisor. /// b is zero. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static int DivRem(this int a, int b, out int result) { return Math.DivRem(a, b, out result); } /// Calculates the quotient of two 64-bit signed integers and also returns the remainder in an output parameter. /// The containing the quotient of the specified numbers. /// The that contains the dividend. /// The that receives the remainder. /// The that contains the divisor. /// b is zero. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static long DivRem(this long a, long b, out long result) { return Math.DivRem(a, b, out result); } /// Returns e raised to the specified power. /// The number e raised to the power value. If value equals or , that value is returned. If value equals , 0 is returned. /// A number specifying a power. public static double Exp(this double value) { return Math.Exp(value); } /// Returns the remainder resulting from the division of a specified number by another specified number. /// A number equal to x - (y Q), where Q is the quotient of x / y rounded to the nearest integer (if x / y falls halfway between two integers, the even integer is returned).If x - (y Q) is zero, the value +0 is returned if x is positive, or -0 if x is negative. If y = 0, (Not-A-Number) is returned. /// A divisor. /// A dividend. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double IEEERemainder(this double x, double y) { return Math.IEEERemainder(x, y); } /// Returns the natural (base e) logarithm of a specified number. /// See for details. /// A number whose logarithm is to be found. public static double Log(this double value) { return Math.Log(value); } /// Returns the logarithm of a specified number in a specified base. /// See for details. /// A number whose logarithm is to be found. /// The base of the logarithm. public static double Log(this double value, double newBase) { return Math.Log(value, newBase); } /// Returns the base 10 logarithm of a specified number. /// See for details. /// A number whose logarithm is to be found. public static double Log10(this double value) { return Math.Log10(value); } /// Returns a specified number raised to the specified power. /// The number x raised to the power y. See for details. /// A double-precision floating-point number that specifies a power. /// A double-precision floating-point number to be raised to a power. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Pow(this double x, double y) { return Math.Pow(x, y); } /// Returns a value indicating the sign of a decimal number. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. public static int Sign(this decimal value) { return Math.Sign(value); } /// Returns a value indicating the sign of a double-precision floating-point number. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. /// value is equal to . public static int Sign(this double value) { return Math.Sign(value); } /// Returns a value indicating the sign of a single-precision floating-point number. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. /// value is equal to . public static int Sign(this float value) { return Math.Sign(value); } /// Returns a value indicating the sign of a 16-bit signed integer. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. public static int Sign(this short value) { return Math.Sign(value); } /// Returns a value indicating the sign of a 32-bit signed integer. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. public static int Sign(this int value) { return Math.Sign(value); } /// Returns a value indicating the sign of a 64-bit signed integer. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. public static int Sign(this long value) { return Math.Sign(value); } /// Returns a value indicating the sign of an 8-bit signed integer. /// A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. /// A signed number. public static int Sign(this sbyte value) { return Math.Sign(value); } /// Returns the square root of a specified number. /// for details. /// A number. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Sqrt(this double value) { return Math.Sqrt(value); } #endregion #region Comparison /// Determines whether a decimal value is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// A decimal value to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this decimal value, decimal a, decimal b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Determines whether a double-precision floating-point value is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// A double-precision floating-point value to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this double value, double a, double b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Determines whether a single-precision floating-point value is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// A double-precision floating-point value to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this float value, float a, float b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Determines whether a 64-bit signed integer is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// A 64-bit signed integer to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this long value, long a, long b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Determines whether a 32-bit signed integer is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// A 32-bit signed integer to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this int value, int a, int b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Determines whether a 16-bit signed integer is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// A 16-bit signed integer to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this short value, short a, short b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Determines whether an 8-bit unsigned integer is inclusively between two values. /// A boolean representing whether value is inclusively between a and b. /// An 8-bit unsigned integer to compare. /// The first bound to compare value against. /// The second bound to compare value against. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static bool IsBetween(this byte value, byte a, byte b) { return a < b ? a <= value && value <= b : b <= value && value <= a; } /// Returns a value indicating whether the specified number evaluates to a value that is not a number (). /// true if value evaluates to ; otherwise, false. /// A double-precision floating-point number. /// 1 public static bool IsNaN(this double value) { return Double.IsNaN(value); } /// Returns a value indicating whether the specified number evaluates to not a number (). /// true if value evaluates to not a number (); otherwise, false. /// A single-precision floating-point number. /// 1 public static bool IsNaN(this float value) { return Single.IsNaN(value); } /// Retrieves the value of the specified number, or zero if it's not a number (). /// The value of the parameter if it doesn't evaluate to ; otherwise, 0.0. /// A double-precision floating-point number. public static double GetValueOrDefault(this double value) { return Double.IsNaN(value) ? 0.0 : value; } /// Retrieves the value of the specified number, or the specified default value if it's not a number (). /// The value of the parameter if it doesn't evaluate to ; otherwise, the parameter. /// A double-precision floating-point number. /// The value to return if returns true. public static double GetValueOrDefault(this double value, double defaultValue) { return Double.IsNaN(value) ? defaultValue : value; } /// Retrieves the value of the specified number, or zero if it's not a number (). /// The value of the parameter if it doesn't evaluate to ; otherwise, 0.0. /// A single-precision floating-point number. public static float GetValueOrDefault(this float value) { return Single.IsNaN(value) ? 0.0f : value; } /// Retrieves the value of the specified number, or the specified default value if it's not a number (). /// The value of the parameter if it doesn't evaluate to ; otherwise, the parameter. /// A single-precision floating-point number. /// The value to return if returns true. public static float GetValueOrDefault(this float value, float defaultValue) { return Single.IsNaN(value) ? defaultValue : value; } #endregion #region Trigonometry /// Returns the angle whose cosine is the specified number. /// An angle, θ, measured in radians, such that 0 ≤ θ ≤ π -or- if value < -1 or value > 1. /// A number representing a cosine, where -1 ≤ value ≤ 1. public static double Acos(this double value) { return Math.Acos(value); } /// Returns the angle whose sine is the specified number. /// An angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2 -or- if value < -1 or value > 1. /// A number representing a sine, where -1 ≤ value ≤ 1. public static double Asin(this double value) { return Math.Asin(value); } /// Returns the angle whose tangent is the specified number. /// An angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2 -or- if value equals , -π/2 rounded to double precision (-1.5707963267949) if value equals , or π/2 rounded to double precision (1.5707963267949) if value equals . /// A number representing a tangent. public static double Atan(this double value) { return Math.Atan(value); } /// Returns the angle whose tangent is the quotient of two specified numbers. /// An angle, θ, measured in radians, such that -π ≤ θ ≤ π, and tan(θ) = y / x, where (x, y) is a point in the Cartesian plane. See for details. /// The y coordinate of a point. /// The x coordinate of a point. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Atan2(this double y, double x) { return Math.Atan2(y, x); } /// Returns the cosine of the specified angle. /// The cosine of angle. /// An angle, measured in radians. public static double Cos(this double angle) { return Math.Cos(angle); } /// Returns the hyperbolic cosine of the specified angle. /// The hyperbolic cosine of angle. If angle is equal to or , is returned. If angle is equal to , is returned. /// An angle, measured in radians. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Cosh(this double angle) { return Math.Cosh(angle); } /// Returns the sine of the specified angle. /// The sine of angle. If angle is equal to , , or , this method returns . /// An angle, measured in radians. public static double Sin(this double angle) { return Math.Sin(angle); } /// Returns the hyperbolic sine of the specified angle. /// The hyperbolic sine of angle. If angle is equal to , , or , this method returns a equal to angle. /// An angle, measured in radians. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Sinh(this double angle) { return Math.Sinh(angle); } /// Returns the tangent of the specified angle. /// The tangent of angle. If angle is equal to , , or , this method returns . /// An angle, measured in radians. public static double Tan(this double angle) { return Math.Tan(angle); } /// Returns the hyperbolic tangent of the specified angle. /// The hyperbolic tangent of angle. If angle is equal to , this method returns -1. If angle is equal to , this method returns 1. If angle is equal to , this method returns . /// An angle, measured in radians. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public static double Tanh(this double angle) { return Math.Tanh(angle); } #endregion #region Sequences /// Generates a sequence of decimal numbers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this decimal start, decimal bound) { var step = start <= bound ? (decimal)1 : (decimal)-1; for (decimal i = start; i < bound; i += step) yield return i; } /// Generates a sequence of decimal numbers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this decimal start, decimal bound, decimal step) { for (decimal i = start; i < bound; i += step) yield return i; } /// Generates a sequence of double-precision floating-point values within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this double start, double bound) { var step = start <= bound ? (double)1 : (double)-1; for (double i = start; i < bound; i += step) yield return i; } /// Generates a sequence of double-precision floating-point values within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this double start, double bound, double step) { for (double i = start; i < bound; i += step) yield return i; } /// Generates a sequence of single-precision floating-point values within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this float start, float bound) { var step = start <= bound ? (float)1 : (float)-1; for (float i = start; i < bound; i += step) yield return i; } /// Generates a sequence of single-precision floating-point values within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this float start, float bound, float step) { for (float i = start; i < bound; i += step) yield return i; } /// Generates a sequence of 64-bit signed integers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this long start, long bound) { var step = start <= bound ? (long)1 : (long)-1; for (long i = start; i < bound; i += step) yield return i; } /// Generates a sequence of 64-bit signed integers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this long start, long bound, long step) { for (long i = start; i < bound; i += step) yield return i; } /// Generates a sequence of 32-bit signed integers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this int start, int bound) { var step = start <= bound ? (int)1 : (int)-1; for (int i = start; i < bound; i += step) yield return i; } /// Generates a sequence of 32-bit signed integers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this int start, int bound, int step) { for (int i = start; i < bound; i += step) yield return i; } /// Generates a sequence of 16-bit signed integers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this short start, short bound) { var step = start <= bound ? (short)1 : (short)-1; for (short i = start; i < bound; i += step) yield return i; } /// Generates a sequence of 16-bit signed integers within a specified range. /// A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. /// The number to start from. /// The number to stop before. public static IEnumerable To(this short start, short bound, short step) { for (short i = start; i < bound; i += step) yield return i; } #endregion } } ================================================ FILE: WpfToolkit/Layout/Extensions/RectExtensions.cs ================================================ using System; using System.Windows; namespace System.Windows.Controls.Extensions { /// /// Provides extension methods for rects. /// internal static class RectExtensions { /// /// Returns the center point of the . /// /// The rect to return the center point of. /// The center of the . public static Point GetCenter(this Rect rect) { return new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2); } /// /// Returns whether the defines a real area in space. /// /// The rect to test. /// true if rect defines an area or point in finite space, which is not the case for or if any of the fields are . public static bool IsDefined(this Rect rect) { return rect.Width >= 0.0 && rect.Height >= 0.0 && rect.Top < Double.PositiveInfinity && rect.Left < Double.PositiveInfinity && (rect.Top > Double.NegativeInfinity || rect.Height == Double.PositiveInfinity) && (rect.Left > Double.NegativeInfinity || rect.Width == Double.PositiveInfinity); } /// /// Indicates whether the specified rectangle intersects with the current rectangle, properly considering the empty rect and infinities. /// /// The current rectangle. /// The rectangle to check. /// true if the specified rectangle intersects with the current rectangle; otherwise, false. public static bool Intersects(this Rect self, Rect rect) { return (self.IsEmpty || rect.IsEmpty) || (self.Width == Double.PositiveInfinity || self.Right >= rect.Left) && (rect.Width == Double.PositiveInfinity || rect.Right >= self.Left) && (self.Height == Double.PositiveInfinity || self.Bottom >= rect.Top) && (rect.Height == Double.PositiveInfinity || rect.Bottom >= self.Top); } } } ================================================ FILE: WpfToolkit/Layout/GlobalSuppressions.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to [###LICENSE_NAME###]. // Please see [###LICENSE_LINK###] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Automation.Peers", Justification = "This is the official namespace for automation peers.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Headered")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Util")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "namescope")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Multi")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Unwatermarked")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Validator's")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "WPF's")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Silverlight")] [assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Multi", Scope = "resource", Target = "System.Windows.Controls.Properties.Resources.resources", Justification = "Follows WPF MultiSelector class naming.")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.Accordion", Justification = "New control for Silverlight")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.AccordionButton", Justification = "Part of Accordion")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.AccordionItem", Justification = "Part of Accordion")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.ExpandableContentControl", Justification = "Part of Accordion")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.LayoutTransformControl")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.SelectionSequence", Justification = "Part of Accordion")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.SelectionMode")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Automation.Peers.AccordionAutomationPeer")] [assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Automation.Peers.AccordionItemAutomationPeer")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowGotFocus(System.Windows.RoutedEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowKeyDown(System.Windows.Input.KeyEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowKeyUp(System.Windows.Input.KeyEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowLostFocus(System.Windows.RoutedEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowMouseEnter(System.Windows.Input.MouseEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowMouseLeave(System.Windows.Input.MouseEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#AllowMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#ClickCount", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#LastClickPosition", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#LastClickTime", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnGotFocusBase()", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnLostFocusBase()", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnMouseEnterBase()", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnMouseLeaveBase()", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnMouseLeftButtonDownBase()", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.InteractionHelper.#OnMouseLeftButtonUpBase()", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.VisualStates.#GetImplementationRoot(System.Windows.DependencyObject)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Controls.VisualStates.#TryGetVisualStateGroup(System.Windows.DependencyObject,System.String)", Justification = "Implementations used by other assemblies.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Controls.Primitives", Justification = "Controls are considered primitives.")] [assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "headered", Justification = "Follows naming used in hierarchy.")] ================================================ FILE: WpfToolkit/Layout/Properties/AssemblyInfo.cs ================================================ using System.Runtime.InteropServices; using System.Windows; // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // In order to begin building localizable applications, set // CultureYouAreCodingWith in your .csproj file // inside a . For example, if you are using US english // in your source files, set the to en-US. Then uncomment // the NeutralResourceLanguage attribute below. Update the "en-US" in // the line below to match the UICulture setting in the project file. // [assembly: NeutralResourcesLanguage("en-US")] // WPF-only settings [assembly: ThemeInfo( ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] ================================================ FILE: WpfToolkit/Layout/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.18213 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Windows.Controls.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Windows.Controls.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to The modification made to the collection lead to an invalid Selection state. Please check the SelectionMode to allow zero or multiple items.. /// internal static string Accordion_InvalidManipulationOfSelectionCollections { get { return ResourceManager.GetString("Accordion_InvalidManipulationOfSelectionCollections", resourceCulture); } } /// /// Looks up a localized string similar to Should not signal a finish when item is not expected to perform an action.. /// internal static string Accordion_OnActionFinish_InvalidFinish { get { return ResourceManager.GetString("Accordion_OnActionFinish_InvalidFinish", resourceCulture); } } /// /// Looks up a localized string similar to Invalid ExpandDirection value '{0}'.. /// internal static string Accordion_OnExpandDirectionPropertyChanged_InvalidValue { get { return ResourceManager.GetString("Accordion_OnExpandDirectionPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set read-only property SelectedIndices.. /// internal static string Accordion_OnSelectedIndicesChanged_InvalidWrite { get { return ResourceManager.GetString("Accordion_OnSelectedIndicesChanged_InvalidWrite", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set read-only property SelectedItems.. /// internal static string Accordion_OnSelectedItemsChanged_InvalidWrite { get { return ResourceManager.GetString("Accordion_OnSelectedItemsChanged_InvalidWrite", resourceCulture); } } /// /// Looks up a localized string similar to Invalid SelectionMode value '{0}'.. /// internal static string Accordion_OnSelectionModePropertyChanged_InvalidValue { get { return ResourceManager.GetString("Accordion_OnSelectionModePropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Invalid SelectionSequence value '{0}'.. /// internal static string Accordion_OnSelectionSequencepropertyChanged_InvalidValue { get { return ResourceManager.GetString("Accordion_OnSelectionSequencepropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Unsupported collection action '{0}'.. /// internal static string Accordion_UnsupportedCollectionAction { get { return ResourceManager.GetString("Accordion_UnsupportedCollectionAction", resourceCulture); } } /// /// Looks up a localized string similar to ContentTargetSize is a read-only value and is set through Accordion.. /// internal static string AccordionItem_InvalidWriteToContentTargetSize { get { return ResourceManager.GetString("AccordionItem_InvalidWriteToContentTargetSize", resourceCulture); } } /// /// Looks up a localized string similar to ExpandDirection is a read-only value and is set through Accordion.. /// internal static string AccordionItem_InvalidWriteToExpandDirection { get { return ResourceManager.GetString("AccordionItem_InvalidWriteToExpandDirection", resourceCulture); } } /// /// Looks up a localized string similar to Cannot modify the IsSelected property while item is locked.. /// internal static string AccordionItem_OnIsSelectedPropertyChanged_InvalidChange { get { return ResourceManager.GetString("AccordionItem_OnIsSelectedPropertyChanged_InvalidChange", resourceCulture); } } /// /// Looks up a localized string similar to Invalid Action '{0}'.. /// internal static string AccordionItem_StartAction_InvalidAction { get { return ResourceManager.GetString("AccordionItem_StartAction_InvalidAction", resourceCulture); } } /// /// Looks up a localized string similar to Cannot start an action when there is no action scheduled.. /// internal static string AccordionItem_StartAction_InvalidCall { get { return ResourceManager.GetString("AccordionItem_StartAction_InvalidCall", resourceCulture); } } /// /// Looks up a localized string similar to Cannot perform operation.. /// internal static string Automation_OperationCannotBePerformed { get { return ResourceManager.GetString("Automation_OperationCannotBePerformed", resourceCulture); } } /// /// Looks up a localized string similar to Invalid ExpandDirection value '{0}'.. /// internal static string Expander_OnExpandDirectionPropertyChanged_InvalidValue { get { return ResourceManager.GetString("Expander_OnExpandDirectionPropertyChanged_InvalidValue", resourceCulture); } } /// /// Looks up a localized string similar to Transition '{0}' was not defined.. /// internal static string TransitioningContentControl_TransitionNotFound { get { return ResourceManager.GetString("TransitioningContentControl_TransitionNotFound", resourceCulture); } } /// /// Looks up a localized string similar to IsTransitioning property is read-only.. /// internal static string TransitiotioningContentControl_IsTransitioningReadOnly { get { return ResourceManager.GetString("TransitiotioningContentControl_IsTransitioningReadOnly", resourceCulture); } } } } ================================================ FILE: WpfToolkit/Layout/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ContentTargetSize is a read-only value and is set through Accordion. Exception thrown when ContentTargetSize on AccordionItem is changed. ExpandDirection is a read-only value and is set through Accordion. Exception thrown when ExpandDirection on AccordionItem is changed. Cannot modify the IsSelected property while item is locked. Exception thrown when the IsSelectedProperty is changed, while the AccordionItem is locked. Invalid Action '{0}'. Exception thrown when the layout action is unknown. Cannot start an action when there is no action scheduled. Exception thrown when no layout action is available but StartAction is called. The modification made to the collection lead to an invalid Selection state. Please check the SelectionMode to allow zero or multiple items. Exception thrown when the SelectionCollections are incorrectly manipulated. Should not signal a finish when item is not expected to perform an action. Exception thrown when an AccordionItem signals the end of an action, but was not registered by Accordion as performing that action. Invalid ExpandDirection value '{0}'. Exception thrown when the ExpandDirection property is provided an invalid value. Cannot set read-only property SelectedIndices. Exception thrown when the SelectedIndices property is improperly set. Cannot set read-only property SelectedItems. Exception thrown when the SelectedItems property is improperly set. Invalid SelectionMode value '{0}'. Exception thrown when the SelectionMode property is provided an invalid value. Invalid SelectionSequence value '{0}'. Exception thrown when the SelectionSequence property is provided an invalid value. Unsupported collection action '{0}'. Exception thrown when the SelectedItems or SelectedIndices collection is changed in a way not supported by Accordion. Cannot perform operation. Exception thrown by automation peers. Invalid ExpandDirection value '{0}'. Exception thrown when the ExpandDirection property is provided an invalid value. Transition '{0}' was not defined. Exception thrown when a Transition is set that is not available. IsTransitioning property is read-only. Exception thrown when IsTransitioning is altered. ================================================ FILE: WpfToolkit/Layout/System.Windows.Controls.Layout.Toolkit.XML ================================================ DotNetProjects.Layout.Toolkit This set of internal extension methods provide general solutions and utilities in a small enough number to not warrant a dedicated extension methods class. Inverts a Matrix. The Invert functionality on the Matrix type is internal to the framework only. Since Matrix is a struct, an out parameter must be presented. The Matrix object. The matrix to return by an output parameter. Returns a value indicating whether the type was successfully inverted. If the determinant is 0.0, then it cannot be inverted and the original instance will remain untouched. An implementation of the Contains member of string that takes in a string comparison. The traditional .NET string Contains member uses StringComparison.Ordinal. The string. The string value to search for. The string comparison type. Returns true when the substring is found. Provides extension methods for trigonometric, logarithmic, and other common mathematical functions. Returns the smallest integer greater than or equal to the specified decimal number. The smallest integer greater than or equal to value. A decimal number. Returns the smallest integer greater than or equal to the specified double-precision floating-point number. The smallest integer greater than or equal to value. If value is equal to , , or , that value is returned. A double-precision floating-point number. Returns the largest integer less than or equal to the specified decimal number. The largest integer less than or equal to value. A decimal number. Returns the largest integer less than or equal to the specified double-precision floating-point number. The largest integer less than or equal to value. If value is equal to , , or , that value is returned. A double-precision floating-point number. Returns the larger of two 8-bit unsigned integers. Parameter a or b, whichever is larger. The first of two 8-bit unsigned integers to compare. The second of two 8-bit unsigned integers to compare. Returns the larger of two decimal numbers. Parameter a or b, whichever is larger. The first of two numbers to compare. The second of two numbers to compare. Returns the larger of two double-precision floating-point numbers. Parameter a or b, whichever is larger. If a, b, or both a and b are equal to , is returned. The first of two double-precision floating-point numbers to compare. The second of two double-precision floating-point numbers to compare. Returns the larger of two single-precision floating-point numbers. Parameter a or b, whichever is larger. If a, or b, or both a and b are equal to , is returned. The first of two single-precision floating-point numbers to compare. The second of two single-precision floating-point numbers to compare. Returns the larger of two 16-bit signed integers. Parameter a or b, whichever is larger. The first of two 16-bit signed integers to compare. The second of two 16-bit signed integers to compare. Returns the larger of two 32-bit signed integers. Parameter a or b, whichever is larger. The first of two 32-bit signed integers to compare. The second of two 32-bit signed integers to compare. Returns the larger of two 64-bit signed integers. Parameter a or b, whichever is larger. The first of two 64-bit signed integers to compare. The second of two 64-bit signed integers to compare. Returns the larger of two 8-bit signed integers. Parameter a or b, whichever is larger. The first of two 8-bit unsigned integers to compare. The second of two 8-bit unsigned integers to compare. Returns the larger of two 16-bit unsigned integers. Parameter a or b, whichever is larger. The first of two 16-bit unsigned integers to compare. The second of two 16-bit unsigned integers to compare. Returns the larger of two 32-bit unsigned integers. Parameter a or b, whichever is larger. The first of two 32-bit unsigned integers to compare. The second of two 32-bit unsigned integers to compare. Returns the larger of two 64-bit unsigned integers. Parameter a or b, whichever is larger. The first of two 64-bit unsigned integers to compare. The second of two 64-bit unsigned integers to compare. Returns the smaller of two 8-bit unsigned integers. Parameter a or b, whichever is smaller. The first of two 8-bit unsigned integers to compare. The second of two 8-bit unsigned integers to compare. Returns the smaller of two decimal numbers. Parameter a or b, whichever is smaller. The first of two numbers to compare. The second of two numbers to compare. Returns the smaller of two double-precision floating-point numbers. Parameter a or b, whichever is smaller. If a, b, or both a and b are equal to , is returned. The first of two double-precision floating-point numbers to compare. The second of two double-precision floating-point numbers to compare. Returns the smaller of two single-precision floating-point numbers. Parameter a or b, whichever is smaller. If a, b, or both a and b are equal to , is returned. The first of two single-precision floating-point numbers to compare. The second of two single-precision floating-point numbers to compare. Returns the smaller of two 16-bit signed integers. Parameter a or b, whichever is smaller. The first of two 16-bit signed integers to compare. The second of two 16-bit signed integers to compare. Returns the smaller of two 32-bit signed integers. Parameter a or b, whichever is smaller. The first of two 32-bit signed integers to compare. The second of two 32-bit signed integers to compare. Returns the smaller of two 64-bit signed integers. Parameter a or b, whichever is smaller. The first of two 64-bit signed integers to compare. The second of two 64-bit signed integers to compare. Returns the smaller of two 8-bit signed integers. Parameter a or b, whichever is smaller. The first of two 8-bit signed integers to compare. The second of two 8-bit signed integers to compare. Returns the smaller of two 16-bit unsigned integers. Parameter a or b, whichever is smaller. The first of two 16-bit unsigned integers to compare. The second of two 16-bit unsigned integers to compare. Returns the smaller of two 32-bit unsigned integers. Parameter a or b, whichever is smaller. The first of two 32-bit unsigned integers to compare. The second of two 32-bit unsigned integers to compare. Returns the smaller of two 64-bit unsigned integers. Parameter a or b, whichever is smaller. The first of two 64-bit unsigned integers to compare. The second of two 64-bit unsigned integers to compare. Returns the value constrained inclusively between two 8-bit unsigned integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 8-bit unsigned integers to compare. The second of two 8-bit unsigned integers to compare. Returns the value constrained inclusively between two decimal numbers. A value between a and b inclusively. The value to restrict between a and b. The first of two numbers to compare. The second of two numbers to compare. Returns the value constrained inclusively between two double-precision floating-point numbers. A value between a and b inclusively. If a, b, or both a and b are equal to , is returned. The value to restrict between a and b. The first of two double-precision floating-point numbers to compare. The second of two double-precision floating-point numbers to compare. Returns the value constrained inclusively between two single-precision floating-point numbers. A value between a and b inclusively. If a, b, or both a and b are equal to , is returned. The value to restrict between a and b. The first of two single-precision floating-point numbers to compare. The second of two single-precision floating-point numbers to compare. Returns the value constrained inclusively between two 16-bit signed integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 16-bit signed integers to compare. The second of two 16-bit signed integers to compare. Returns the value constrained inclusively between two 32-bit signed integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 32-bit signed integers to compare. The second of two 32-bit signed integers to compare. Returns the value constrained inclusively between two 64-bit signed integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 64-bit signed integers to compare. The second of two 64-bit signed integers to compare. Returns the value constrained inclusively between two 8-bit signed integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 8-bit signed integers to compare. The second of two 8-bit signed integers to compare. Returns the value constrained inclusively between two 16-bit unsigned integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 16-bit unsigned integers to compare. The second of two 16-bit unsigned integers to compare. Returns the value constrained inclusively between two 32-bit unsigned integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 32-bit unsigned integers to compare. The second of two 32-bit unsigned integers to compare. Returns the value constrained inclusively between two 64-bit unsigned integers. A value between a and b inclusively. The value to restrict between a and b. The first of two 64-bit unsigned integers to compare. The second of two 64-bit unsigned integers to compare. Rounds a decimal value to the nearest integer. The integer nearest parameter value. If value is halfway between two integers, one of which is even and the other odd, then the even number is returned. A decimal number to be rounded. The result is outside the range of a . Rounds a double-precision floating-point value to the nearest integer. The integer nearest value. If value is halfway between two integers, one of which is even and the other odd, then the even number is returned. A double-precision floating-point number to be rounded. Rounds a decimal value to a specified precision. The number nearest value with a precision equal to decimals. If value is halfway between two numbers, one of which is even and the other odd, then the even number is returned. If the precision of value is less than decimals, then value is returned unchanged. A decimal number to be rounded. The number of significant decimal places (precision) in the return value. The result is outside the range of a . decimals is less than 0 or greater than 28. Rounds a decimal value to the nearest integer. A parameter specifies how to round the value if it is midway between two other numbers. The integer nearest value. If value is halfway between two numbers, one of which is even and the other odd, then mode determines which of the two is returned. A decimal number to be rounded. Specification for how to round value if it is midway between two other numbers. The result is outside the range of a . mode is not a valid value of . Rounds a double-precision floating-point value to the specified precision. The number nearest value with a precision equal to digits. If value is halfway between two numbers, one of which is even and the other odd, then the even number is returned. If the precision of value is less than digits, then value is returned unchanged. The number of significant digits (precision) in the return value. A double-precision floating-point number to be rounded. digits is less than 0 or greater than 15. Rounds a double-precision floating-point value to the nearest integer. A parameter specifies how to round the value if it is midway between two other numbers. The integer nearest value. If value is halfway between two integers, one of which is even and the other odd, then mode determines which of the two is returned. Specification for how to round value if it is midway between two other numbers. A double-precision floating-point number to be rounded. mode is not a valid value of . Rounds a decimal value to a specified precision. A parameter specifies how to round the value if it is midway between two other numbers. The number nearest value with a precision equal to decimals. If value is halfway between two numbers, one of which is even and the other odd, then mode determines which of the two numbers is returned. If the precision of value is less than decimals, then value is returned unchanged. A decimal number to be rounded. Specification for how to round value if it is midway between two other numbers. The number of significant decimal places (precision) in the return value. The result is outside the range of a . decimals is less than 0 or greater than 28. mode is not a valid value of . Rounds a double-precision floating-point value to the specified precision. A parameter specifies how to round the value if it is midway between two other numbers. The number nearest value with a precision equal to digits. If value is halfway between two numbers, one of which is even and the other odd, then the mode parameter determines which number is returned. If the precision of value is less than digits, then value is returned unchanged. Specification for how to round value if it is midway between two other numbers. The number of significant digits (precision) in the return value. A double-precision floating-point number to be rounded. digits is less than 0 or greater than 15. mode is not a valid value of . Rounds a decimal value to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A decimal number to be rounded. The factor to round the value to. Rounds a double-precision floating-point value to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A double-precision floating-point number to be rounded. The factor to round the value to. Rounds a single-precision floating-point value to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A single-precision floating-point number to be rounded. The factor to round the value to. Rounds a 64-bit signed integer to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A 64-bit signed integer to be rounded. The factor to round the value to. Rounds a 32-bit signed integer to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A 32-bit signed integer to be rounded. The factor to round the value to. Rounds a 16-bit signed integer to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A 16-bit signed integer to be rounded. The factor to round the value to. Rounds a 8-bit signed integer to the next multiple of the specified factor. The nearest multiple of factor that is greater than or equal to value. A 8-bit signed integer to be rounded. The factor to round the value to. Rounds a decimal value to the next integer. The nearest integer that is greater than or equal to value. A decimal number to be rounded. Rounds a double-precision floating-point value to the next integer. The nearest integer that is greater than or equal to value. A double-precision floating-point number to be rounded. Rounds a single-precision floating-point value to the next integer. The nearest integer that is greater than or equal to value. A single-precision floating-point number to be rounded. Rounds a 64-bit signed integer to the next integer. The nearest integer that is greater than or equal to value. A 64-bit signed integer to be rounded. Rounds a 32-bit signed integer to the next integer. The nearest integer that is greater than or equal to value. A 32-bit signed integer to be rounded. Rounds a 16-bit signed integer to the next integer. The nearest integer that is greater than or equal to value. A 16-bit signed integer to be rounded. Rounds a 8-bit signed integer to the next integer. The nearest integer that is greater than or equal to value. A 8-bit signed integer to be rounded. Rounds a decimal value to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. A decimal number to be rounded. The factor to round the value to. Rounds a double-precision floating-point value to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. A double-precision floating-point number to be rounded. The factor to round the value to. Rounds a single-precision floating-point value to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. A single-precision floating-point number to be rounded. The factor to round the value to. Rounds a 64-bit signed integer to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. A 64-bit signed integer to be rounded. The factor to round the value to. Rounds a 32-bit signed integer to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. A 32-bit signed integer to be rounded. The factor to round the value to. Rounds a 16-bit signed integer to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. A 16-bit signed integer to be rounded. The factor to round the value to. Rounds an 8-bit unsigned integer to the previous multiple of the specified factor. The nearest multiple of factor that is less than or equal to value. An 8-bit unsigned integer to be rounded. The factor to round the value to. Rounds a decimal value to the previous integer. The nearest integer that is less than or equal to value. A decimal number to be rounded. Rounds a double-precision floating-point value to the previous integer. The nearest integer that is less than or equal to value. A double-precision floating-point number to be rounded. Rounds a single-precision floating-point value to the previous integer. The nearest integer that is less than or equal to value. A single-precision floating-point number to be rounded. Rounds a 64-bit signed integer to the previous integer. The nearest integer that is less than or equal to value. A 64-bit signed integer to be rounded. Rounds a 32-bit signed integer to the previous integer. The nearest integer that is less than or equal to value. A 32-bit signed integer to be rounded. Rounds a 16-bit signed integer to the previous integer. The nearest integer that is less than or equal to value. A 16-bit signed integer to be rounded. Rounds an 8-bit unsigned integer to the previous integer. The nearest integer that is less than or equal to value. An 8-bit unsigned integer to be rounded. Calculates the integral part of a specified decimal number. The integral part of value; that is, the number that remains after any fractional digits have been discarded. A number to truncate. Calculates the integral part of a specified double-precision floating-point number. The integral part of value; that is, the number that remains after any fractional digits have been discarded. A number to truncate. Returns the absolute value of a number. A , x, such that 0 ≤ x ≤ . A number in the range ≤ value ≤ . Returns the absolute value of a double-precision floating-point number. A double-precision floating-point number, x, such that 0 ≤ x ≤ . A number in the range ≤ value ≤ . Returns the absolute value of a single-precision floating-point number. A single-precision floating-point number, x, such that 0 ≤ x ≤ . A number in the range ≤ value ≤ . Returns the absolute value of a 16-bit signed integer. A 16-bit signed integer, x, such that 0 ≤ x ≤ . A number in the range < value ≤ . value equals . Returns the absolute value of a 32-bit signed integer. A 32-bit signed integer, x, such that 0 ≤ x ≤ . A number in the range < value ≤ . value equals . Returns the absolute value of a 64-bit signed integer. A 64-bit signed integer, x, such that 0 ≤ x ≤ . A number in the range < value ≤ . value equals . Returns the absolute value of an 8-bit signed integer. An 8-bit signed integer, x, such that 0 ≤ x ≤ . A number in the range < value ≤ . value equals . Produces the full product of two 32-bit numbers. The containing the product of the specified numbers. The first to multiply. The second to multiply. Calculates the quotient of two 32-bit signed integers and also returns the remainder in an output parameter. The containing the quotient of the specified numbers. The that contains the dividend. The that receives the remainder. The that contains the divisor. b is zero. Calculates the quotient of two 64-bit signed integers and also returns the remainder in an output parameter. The containing the quotient of the specified numbers. The that contains the dividend. The that receives the remainder. The that contains the divisor. b is zero. Returns e raised to the specified power. The number e raised to the power value. If value equals or , that value is returned. If value equals , 0 is returned. A number specifying a power. Returns the remainder resulting from the division of a specified number by another specified number. A number equal to x - (y Q), where Q is the quotient of x / y rounded to the nearest integer (if x / y falls halfway between two integers, the even integer is returned).If x - (y Q) is zero, the value +0 is returned if x is positive, or -0 if x is negative. If y = 0, (Not-A-Number) is returned. A divisor. A dividend. Returns the natural (base e) logarithm of a specified number. See for details. A number whose logarithm is to be found. Returns the logarithm of a specified number in a specified base. See for details. A number whose logarithm is to be found. The base of the logarithm. Returns the base 10 logarithm of a specified number. See for details. A number whose logarithm is to be found. Returns a specified number raised to the specified power. The number x raised to the power y. See for details. A double-precision floating-point number that specifies a power. A double-precision floating-point number to be raised to a power. Returns a value indicating the sign of a decimal number. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. Returns a value indicating the sign of a double-precision floating-point number. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. value is equal to . Returns a value indicating the sign of a single-precision floating-point number. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. value is equal to . Returns a value indicating the sign of a 16-bit signed integer. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. Returns a value indicating the sign of a 32-bit signed integer. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. Returns a value indicating the sign of a 64-bit signed integer. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. Returns a value indicating the sign of an 8-bit signed integer. A number indicating the sign of value.Number Description -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero. A signed number. Returns the square root of a specified number. for details. A number. Determines whether a decimal value is inclusively between two values. A boolean representing whether value is inclusively between a and b. A decimal value to compare. The first bound to compare value against. The second bound to compare value against. Determines whether a double-precision floating-point value is inclusively between two values. A boolean representing whether value is inclusively between a and b. A double-precision floating-point value to compare. The first bound to compare value against. The second bound to compare value against. Determines whether a single-precision floating-point value is inclusively between two values. A boolean representing whether value is inclusively between a and b. A double-precision floating-point value to compare. The first bound to compare value against. The second bound to compare value against. Determines whether a 64-bit signed integer is inclusively between two values. A boolean representing whether value is inclusively between a and b. A 64-bit signed integer to compare. The first bound to compare value against. The second bound to compare value against. Determines whether a 32-bit signed integer is inclusively between two values. A boolean representing whether value is inclusively between a and b. A 32-bit signed integer to compare. The first bound to compare value against. The second bound to compare value against. Determines whether a 16-bit signed integer is inclusively between two values. A boolean representing whether value is inclusively between a and b. A 16-bit signed integer to compare. The first bound to compare value against. The second bound to compare value against. Determines whether an 8-bit unsigned integer is inclusively between two values. A boolean representing whether value is inclusively between a and b. An 8-bit unsigned integer to compare. The first bound to compare value against. The second bound to compare value against. Returns a value indicating whether the specified number evaluates to a value that is not a number (). true if value evaluates to ; otherwise, false. A double-precision floating-point number. 1 Returns a value indicating whether the specified number evaluates to not a number (). true if value evaluates to not a number (); otherwise, false. A single-precision floating-point number. 1 Retrieves the value of the specified number, or zero if it's not a number (). The value of the parameter if it doesn't evaluate to ; otherwise, 0.0. A double-precision floating-point number. Retrieves the value of the specified number, or the specified default value if it's not a number (). The value of the parameter if it doesn't evaluate to ; otherwise, the parameter. A double-precision floating-point number. The value to return if returns true. Retrieves the value of the specified number, or zero if it's not a number (). The value of the parameter if it doesn't evaluate to ; otherwise, 0.0. A single-precision floating-point number. Retrieves the value of the specified number, or the specified default value if it's not a number (). The value of the parameter if it doesn't evaluate to ; otherwise, the parameter. A single-precision floating-point number. The value to return if returns true. Returns the angle whose cosine is the specified number. An angle, θ, measured in radians, such that 0 ≤ θ ≤ π -or- if value < -1 or value > 1. A number representing a cosine, where -1 ≤ value ≤ 1. Returns the angle whose sine is the specified number. An angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2 -or- if value < -1 or value > 1. A number representing a sine, where -1 ≤ value ≤ 1. Returns the angle whose tangent is the specified number. An angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2 -or- if value equals , -π/2 rounded to double precision (-1.5707963267949) if value equals , or π/2 rounded to double precision (1.5707963267949) if value equals . A number representing a tangent. Returns the angle whose tangent is the quotient of two specified numbers. An angle, θ, measured in radians, such that -π ≤ θ ≤ π, and tan(θ) = y / x, where (x, y) is a point in the Cartesian plane. See for details. The y coordinate of a point. The x coordinate of a point. Returns the cosine of the specified angle. The cosine of angle. An angle, measured in radians. Returns the hyperbolic cosine of the specified angle. The hyperbolic cosine of angle. If angle is equal to or , is returned. If angle is equal to , is returned. An angle, measured in radians. Returns the sine of the specified angle. The sine of angle. If angle is equal to , , or , this method returns . An angle, measured in radians. Returns the hyperbolic sine of the specified angle. The hyperbolic sine of angle. If angle is equal to , , or , this method returns a equal to angle. An angle, measured in radians. Returns the tangent of the specified angle. The tangent of angle. If angle is equal to , , or , this method returns . An angle, measured in radians. Returns the hyperbolic tangent of the specified angle. The hyperbolic tangent of angle. If angle is equal to , this method returns -1. If angle is equal to , this method returns 1. If angle is equal to , this method returns . An angle, measured in radians. Generates a sequence of decimal numbers within a specified range. A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. The number to start from. The number to stop before. Generates a sequence of decimal numbers within a specified range. A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. The number to start from. The number to stop before. Generates a sequence of double-precision floating-point values within a specified range. A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. The number to start from. The number to stop before. Generates a sequence of double-precision floating-point values within a specified range. A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. The number to start from. The number to stop before. Generates a sequence of single-precision floating-point values within a specified range. A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. The number to start from. The number to stop before. Generates a sequence of single-precision floating-point values within a specified range. A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. The number to start from. The number to stop before. Generates a sequence of 64-bit signed integers within a specified range. A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. The number to start from. The number to stop before. Generates a sequence of 64-bit signed integers within a specified range. A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. The number to start from. The number to stop before. Generates a sequence of 32-bit signed integers within a specified range. A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. The number to start from. The number to stop before. Generates a sequence of 32-bit signed integers within a specified range. A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. The number to start from. The number to stop before. Generates a sequence of 16-bit signed integers within a specified range. A sequence of numbers from start to (but not including) bound where each number is 1 greater than the previous number. The number to start from. The number to stop before. Generates a sequence of 16-bit signed integers within a specified range. A sequence of numbers from start to (but not including) bound where each number is a given step greater than the previous number. The number to start from. The number to stop before. Provides extension methods for rects. Returns the center point of the . The rect to return the center point of. The center of the . Returns whether the defines a real area in space. The rect to test. true if rect defines an area or point in finite space, which is not the case for or if any of the fields are . Indicates whether the specified rectangle intersects with the current rectangle, properly considering the empty rect and infinities. The current rectangle. The rectangle to check. true if the specified rectangle intersects with the current rectangle; otherwise, false. The InteractionHelper provides controls with support for all of the common interactions like mouse movement, mouse clicks, key presses, etc., and also incorporates proper event semantics when the control is disabled. The threshold used to determine whether two clicks are temporally local and considered a double click (or triple, quadruple, etc.). 500 milliseconds is the default double click value on Windows. This value would ideally be pulled form the system settings. The threshold used to determine whether two clicks are spatially local and considered a double click (or triple, quadruple, etc.) in pixels squared. We use pixels squared so that we can compare to the distance delta without taking a square root. Gets the control the InteractionHelper is targeting. Gets a value indicating whether the control has focus. Gets a value indicating whether the mouse is over the control. Gets a value indicating whether the read-only property is set. Gets a value indicating whether the mouse button is pressed down over the control. Gets or sets the last time the control was clicked. The value is stored as Utc time because it is slightly more performant than converting to local time. Gets or sets the mouse position of the last click. The value is relative to the control. Gets the number of times the control was clicked. Reference used to call UpdateVisualState on the base class. Initializes a new instance of the InteractionHelper class. Control receiving interaction. Update the visual state of the control. A value indicating whether to automatically generate transitions to the new state, or instantly transition to the new state. UpdateVisualState works differently than the rest of the injected functionality. Most of the other events are overridden by the calling class which calls Allow, does what it wants, and then calls Base. UpdateVisualState is the opposite because a number of the methods in InteractionHelper need to trigger it in the calling class. We do this using the IUpdateVisualState internal interface. Update the visual state of the control. A value indicating whether to automatically generate transitions to the new state, or instantly transition to the new state. Handle the control's Loaded event. The control. Event arguments. Handle changes to the control's IsEnabled property. The control. Event arguments. Handles changes to the control's IsReadOnly property. The value of the property. Update the visual state of the control when its template is changed. Check if the control's GotFocus event should be handled. Event arguments. A value indicating whether the event should be handled. Base implementation of the virtual GotFocus event handler. Check if the control's LostFocus event should be handled. Event arguments. A value indicating whether the event should be handled. Base implementation of the virtual LostFocus event handler. Check if the control's MouseEnter event should be handled. Event arguments. A value indicating whether the event should be handled. Base implementation of the virtual MouseEnter event handler. Check if the control's MouseLeave event should be handled. Event arguments. A value indicating whether the event should be handled. Base implementation of the virtual MouseLeave event handler. Check if the control's MouseLeftButtonDown event should be handled. Event arguments. A value indicating whether the event should be handled. Base implementation of the virtual MouseLeftButtonDown event handler. Check if the control's MouseLeftButtonUp event should be handled. Event arguments. A value indicating whether the event should be handled. Base implementation of the virtual MouseLeftButtonUp event handler. Check if the control's KeyDown event should be handled. Event arguments. A value indicating whether the event should be handled. Check if the control's KeyUp event should be handled. Event arguments. A value indicating whether the event should be handled. The ItemContainerGenerator provides useful utilities for ItemsControls. Preview Gets or sets the ItemsControl being tracked by the ItemContainerGenerator. A Panel that is used as the ItemsHost of the ItemsControl. This property will only be valid when the ItemsControl is live in the tree and has generated containers for some of its items. Gets a Panel that is used as the ItemsHost of the ItemsControl. This property will only be valid when the ItemsControl is live in the tree and has generated containers for some of its items. A ScrollViewer that is used to scroll the items in the ItemsHost. Gets a ScrollViewer that is used to scroll the items in the ItemsHost. Initializes a new instance of the ItemContainerGenerator. The ItemsControl being tracked by the ItemContainerGenerator. Apply a control template to the ItemsControl. Prepares the specified container to display the specified item. Container element used to display the specified item. The ItemContainerStyle for the parent ItemsControl. Update the style of any generated items when the ItemContainerStyle has been changed. The ItemContainerStyle. Silverlight does not support setting a Style multiple times, so we only attempt to set styles on elements whose style hasn't already been set. Scroll the desired element into the ScrollHost's viewport. Element to scroll into view. The IUpdateVisualState interface is used to provide the InteractionHelper with access to the type's UpdateVisualState method. Update the visual state of the control. A value indicating whether to automatically generate transitions to the new state, or instantly transition to the new state. Names and helpers for visual states in the controls. Common state group. Normal state of the Common state group. Normal state of the Common state group. MouseOver state of the Common state group. Pressed state of the Common state group. Disabled state of the Common state group. Focus state group. Unfocused state of the Focus state group. Focused state of the Focus state group. Selection state group. Selected state of the Selection state group. Unselected state of the Selection state group. Selected inactive state of the Selection state group. Expansion state group. Expanded state of the Expansion state group. Collapsed state of the Expansion state group. Popup state group. Opened state of the Popup state group. Closed state of the Popup state group. ValidationStates state group. The valid state for the ValidationStates group. Invalid, focused state for the ValidationStates group. Invalid, unfocused state for the ValidationStates group. ExpandDirection state group. Down expand direction state of ExpandDirection state group. Up expand direction state of ExpandDirection state group. Left expand direction state of ExpandDirection state group. Right expand direction state of ExpandDirection state group. HasItems state group. HasItems state of the HasItems state group. NoItems state of the HasItems state group. Increment state group. State enabled for increment group. State disabled for increment group. Decrement state group. State enabled for decrement group. State disabled for decrement group. InteractionMode state group. Edit of the DisplayMode state group. Display of the DisplayMode state group. DisplayMode state group. Edit of the DisplayMode state group. Display of the DisplayMode state group. Active state. Inactive state. Active state group. Non-watermarked state. Watermarked state. Watermark state group. Unfocused state for Calendar Buttons. Focused state for Calendar Buttons. CalendarButtons Focus state group. Busy state for BusyIndicator. Idle state for BusyIndicator. Busyness group name. Visible state name for BusyIndicator. Hidden state name for BusyIndicator. BusyDisplay group. Use VisualStateManager to change the visual state of the control. Control whose visual state is being changed. A value indicating whether to use transitions when updating the visual state, or to snap directly to the new visual state. Ordered list of state names and fallback states to transition into. Only the first state to be found will be used. Gets the implementation root of the Control. The DependencyObject. Implements Silverlight's corresponding internal property on Control. Returns the implementation root or null. This method tries to get the named VisualStateGroup for the dependency object. The provided object's ImplementationRoot will be looked up in this call. The dependency object. The visual state group's name. Returns null or the VisualStateGroup object. A static class providing methods for working with the visual tree. Retrieves all the visual children of a framework element. The parent framework element. The visual children of the framework element. Retrieves all the logical children of a framework element using a breadth-first search. A visual element is assumed to be a logical child of another visual element if they are in the same namescope. For performance reasons this method manually manages the queue instead of using recursion. The parent framework element. The logical children of the framework element. Implements a weak event listener that allows the owner to be garbage collected if its only remaining link is an event handler. Type of instance listening for the event. Type of source for the event. Type of event arguments for the event. WeakReference to the instance listening for the event. Gets or sets the method to call when the event fires. Gets or sets the method to call when detaching from the event. Initializes a new instances of the WeakEventListener class. Instance subscribing to the event. Handler for the subscribed event calls OnEventAction to handle it. Event source. Event arguments. Detaches from the subscribed event. Represents a collection of collapsed and expanded AccordionItem controls. Preview The items that are currently waiting to perform an action. An action can be expanding, resizing or collapsing. The name used to indicate AccordionButtonStyle property. Determines whether the SelectedItemsProperty may be written. Determines whether the SelectedIndicesProperty may be written. Indicates that changes to the SelectedIndices collection should be ignored. Indicates that changes to the SelectedItems collection should be ignored. Determines whether we are currently in the SelectedItems Collection Changed handling. Determines whether we are currently in the SelectedIndices Collection Changed handling. The item that is currently visually performing an action. An action can be expanding, resizing or collapsing. Gets the ItemsControlHelper that is associated with this control. Gets a value indicating whether this instance is currently resizing. True if this instance is resizing; otherwise, false. Gets or sets the helper that provides all of the standard interaction functionality. Gets or sets the ExpandDirection property of each AccordionItem in the Accordion control and the direction in which the Accordion does layout. Setting the ExpandDirection will set the expand direction on the accordionItems. Identifies the ExpandDirection dependency property. ExpandDirectionProperty property changed handler. Accordion that changed its ExpandDirection. Event arguments. Gets or sets the AccordionSelectionMode used to determine the minimum and maximum selected AccordionItems allowed in the Accordion. Identifies the SelectionMode dependency property. SelectionModeProperty property changed handler. Accordion that changed its SelectionMode. Event arguments. Gets a value indicating whether at least one item is selected at all times. Gets a value indicating whether at most one item is selected at all times. Gets or sets the selected item. The default value is null. When multiple items are allowed (IsMaximumOneSelected false), return the first of the selectedItems. Identifies the SelectedItem dependency property. SelectedItemProperty property changed handler. Accordion that changed its SelectedItem. Event arguments. Determines whether the new value can be selected. The new value. True if this item can be selected; otherwise, false. Nested level for SelectedItemCoercion. Gets or sets the index of the currently selected AccordionItem. Identifies the SelectedIndex dependency property. SelectedIndexProperty property changed handler. Accordion that changed its SelectedIndex. Event arguments. Determines whether the new value can be selected. The new value. True if this item can be selected; otherwise, false. Coercion level. Gets or sets the SelectionSequence used to determine the order of AccordionItem selection. Identifies the SelectionSequence dependency property. Called when SelectionSequenceProperty changed. Accordion that changed its SelectionSequence property. The instance containing the event data. Gets the selected items. Does not allow setting. Identifies the SelectedItems dependency property. Property changed handler of SelectedItems. Accordion that changed the collection. Event arguments. Gets the indices of the currently selected AccordionItems. Identifies the SelectedIndices dependency property. Property changed handler of SelectedIndices. Accordion that changed the collection. Event arguments. Gets or sets the Style that is applied to AccordionButton elements in the AccordionItems. Identifies the AccordionButtonStyle dependency property. AccordionButtonStyleProperty property changed handler. Accordion that changed its AccordionButtonStyle. Event arguments. Gets or sets the DataTemplate used to display the content of each generated AccordionItem. Either ContentTemplate or ItemTemplate is used. Setting both will result in an exception. Identifies the ContentTemplate dependency property. Occurs when the SelectedItem or SelectedItems property value changes. Occurs when the SelectedItem or SelectedItems property value changes. Occurs when the SelectedItems collection changes. Static constructor Initializes a new instance of the class. Builds the visual tree for the Accordion control when a new template is applied. Returns a AccordionAutomationPeer for use by the Silverlight automation infrastructure. A AccordionAutomationPeer object for the Accordion. Creates or identifies the element that is used to display the given item. The element that is used to display the given item. Determines if the specified item is (or is eligible to be) its own container. The item to check. True if the item is (or is eligible to be) its own container; otherwise, false. Prepares the specified element to display the specified item. The element used to display the specified item. The item to display. Undoes the effects of the method. The container element. The item that should be cleared. Invoked when the property changes. Information about the change. Initializes the SelectedItem property when a new ItemsSource is set. Called when an AccordionItem is unselected. The accordion item that was unselected. Unselects the item. The index of the item that will be unselected. The item that will be unselected. Can be null. Called when an AccordionItem selected. The accordion item that was selected. Selects the item. The index of the item to select. Changes the selected item, by unselecting and selecting where necessary. The old index. The new index. Called when selected items collection changed. The sender. The instance containing the event data. Called when selected indices collection changed. The sender. The instance containing the event data. Gets an item that is suitable for selection. Index that should not be considered if possible. An item that should be selected. This could be nonCandidateIndex, if no other possibility was found. Selects all the AccordionItems in the Accordion control. If the Accordion SelectionMode is OneOrMore or ZeroOrMore all AccordionItems would be selected. If the Accordion SelectionMode is One or ZeroOrOne all items would be selected and unselected. Only the last AccordionItem would remain selected. Unselects all the AccordionItems in the Accordion control. If the Accordion SelectionMode is Zero or ZeroOrMore all AccordionItems would be Unselected. If SelectionMode is One or OneOrMode than all items would be Unselected and selected. Only the first AccordionItem would still be selected. Updates all accordionItems to be selected or unselected. True to select all items, false to unselect. Will not attempt to change a locked accordionItem. Sets the locked properties on all the items. Raises the SelectedItemChanged event when the SelectedItem property value changes. The instance containing the event data. Raise the SelectedItemsCollectionChanged event. The instance containing the event data. This event is raised after the changes to the collection have been processed. Called when the size of the Accordion changes. The sender. The instance containing the event data. Called when size of a Header on the item changes. The item whose Header changed. Allows an AccordionItem to signal the need for a visual action (resize, collapse, expand). The AccordionItem that signals for a schedule. The action it is scheduling for. True if the item is allowed to proceed without scheduling, false if the item needs to wait for a signal to execute the action. Signals the finish of an action by an item. The AccordionItem that finishes an action. An AccordionItem should always signal a finish, for this call will start the next scheduled action. Starts the next action in the list, in a particular order. An AccordionItem is should always signal that it is finished with an action. Determines and sets the height of the accordion items. Gets a value indicating whether the accordion fills width. Gets a value indicating whether the accordion fills height. Sets the orientation of the panel. Provides handling for the GotFocus event. The data for the event. Provides handling for the LostFocus event. The data for the event. Provides handling for the MouseEnter event. The data for the event. Provides handling for the MouseLeave event. The data for the event. Provides handling for the MouseLeftButtonDown event. The data for the event. Called before the MouseLeftButtonUp event occurs. The data for the event. Update the visual state of the control. A value indicating whether to automatically generate transitions to the new state, or instantly transition to the new state. Update the current visual state of the button. True to use transitions when updating the visual state, false to snap directly to the new visual state. Determines the action the AccordionItem will perform. Preview No action will be performed. A collapse will be performed. An expand will be performed. A resize will be performed. Represents the header for an accordion item. By creating a seperate control, there is more flexibility in the templating possibilities. Preview Gets or sets a reference to the parent AccordionItem of an AccordionButton. The parent accordion item. Static constructor Initializes a new instance of the class. Updates the state of the visual. If set to true use transitions. The header will follow the parent accordionitem states. Gets or sets the direction in which the ExpandableContentControl content window opens. Identifies the RevealMode dependency property. Gets a value indicating whether the content should be revealed horizontally. Gets a value indicating whether the content should be revealed verticaly. Gets or sets the relative percentage of the content that is currently visible. A percentage of 1 corresponds to the complete TargetSize. Identifies the Percentage dependency property. Gets the content current visible size. Represents a control that displays a header and has a collapsible content window. Preview The name of the ExpanderButton template part. The name of the ExpandSite template part. The ExpanderButton template part is a templated ToggleButton that's used to select and unselect this AccordionItem. Gets or sets the ExpanderButton template part. BackingField for the ExpandSite property. Gets or sets the expand site template part. Indicates that the control is currently executing an action. Determines whether the ExpandDirection property may be written. Gets the direction in which the AccordionItem content window opens. Identifies the ExpandDirection dependency property. ExpandDirectionProperty PropertyChangedCallback call back static function. This function validates the new value before calling virtual function OnExpandDirectionChanged. Expander object whose ExpandDirection property is changed. DependencyPropertyChangedEventArgs which contains the old and new values. Gets or sets a value indicating whether the AccordionItem is selected and its content window is visible. Identifies the IsSelected dependency property. SelectedProperty PropertyChangedCallback static function. Expander object whose Expanded property is changed. DependencyPropertyChangedEventArgs which contains the old and new values. Nested level for IsSelectedCoercion. Gets a value indicating whether the AccordionItem cannot be selected by the user. True if this instance is locked; otherwise, false. The IsSelected property may not be changed when the AccordionItem is locked. Locking occurs when the item is the first in the list, the SelectionMode of Accordion requires atleast one selected AccordionItem and the AccordionItem is currently selected. BackingField for IsLocked. Gets or sets the Style used by AccordionButton. Identifies the AccordionButtonStyle dependency property. AccordionButtonStyleProperty property changed handler. AccordionItem that changed its AccordionButtonStyle. Event arguments. Called when AccordionButtonStyle is changed. The old style. The new style. Gets or sets the Style used by ExpandableContentControl. Identifies the ExpandableContentControlStyle dependency property. ExpandableContentControlStyleProperty property changed handler. AccordionItem that changed its ExpandableContentControlStyle. Event arguments. Called when ExpandableContentControlStyle is changed. The old style. The new style. Determines whether it is allowed to set the ContentTargetSize property. Gets the Size that the content will animate to. Identifies the ContentTargetSize dependency property. ContentTargetSizeProperty property changed handler. AccordionItem that changed its ContentTargetSize. Event arguments. Gets or sets a reference to the parent Accordion of an AccordionItem. Gets the scheduled action. The scheduled action. Occurs when the accordionItem is selected. Occurs when the accordionItem is selected. Occurs when the accordionItem is unselected. Occurs when the accordionItem is unselected. Static constructor Initializes a new instance of the AccordionItem class. Called when the size of the control changes. The sender. The instance containing the event data. Gets the relevant size of the current content. Schedules the specified action. The action to be performed. Starts an action, such as resize, collapse or expand. Gets a value indicating whether the AccordionItem fills width. Gets a value indicating whether the AccordionItem fills height. Builds the visual tree for the AccordionItem control when a new template is applied. Returns a AccordionItemAutomationPeer for use by the Silverlight automation infrastructure. A AccordionItemAutomationPeer object for the AccordionItem. Prepares the specified container to display the specified item. Container element used to display the specified item. Specified item to display. The parent ItemsControl. The ItemContainerStyle for the parent ItemsControl. Check whether a control has the default value for a property. The control to check. The property to check. True if the property has the default value; false otherwise. Provides handling for the KeyDown event. Key event args. Raises the Selected event when the IsSelected property changes from false to true. Raises the Unselected event when the IsSelected property changes from true to false. Handle changes to the IsSelected property. Event arguments. Handle ExpanderButton's click event. The ExpanderButton in template. Routed event arg. Gets or sets the helper that provides all of the standard interaction functionality. Provides handling for the GotFocus event. The data for the event. Provides handling for the LostFocus event. The data for the event. Provides handling for the MouseEnter event. The data for the event. Provides handling for the MouseLeave event. The data for the event. Provides handling for the MouseLeftButtonDown event. The data for the event. Called before the MouseLeftButtonUp event occurs. The data for the event. Update the visual state of the control. A value indicating whether to automatically generate transitions to the new state, or instantly transition to the new state. Update the current visual state of the button. True to use transitions when updating the visual state, false to snap directly to the new visual state. Defines the minimum and maximum number of selected items allowed in an Accordion control. Preview Exactly one item must be selected in the Accordion. At least one item must be selected in the Accordion. No more than one item can be selected in the accordion. Any number of items can be selected in the Accordion. Determines the order in which visual states are set. Preview Collapses are set before expansions. No delays, all states are set immediately. A strongly-typed resource class, for looking up localized strings, etc. Returns the cached ResourceManager instance used by this class. Overrides the current thread's CurrentUICulture property for all resource lookups using this strongly typed resource class. Looks up a localized string similar to The modification made to the collection lead to an invalid Selection state. Please check the SelectionMode to allow zero or multiple items.. Looks up a localized string similar to Should not signal a finish when item is not expected to perform an action.. Looks up a localized string similar to Invalid ExpandDirection value '{0}'.. Looks up a localized string similar to Cannot set read-only property SelectedIndices.. Looks up a localized string similar to Cannot set read-only property SelectedItems.. Looks up a localized string similar to Invalid SelectionMode value '{0}'.. Looks up a localized string similar to Invalid SelectionSequence value '{0}'.. Looks up a localized string similar to Unsupported collection action '{0}'.. Looks up a localized string similar to ContentTargetSize is a read-only value and is set through Accordion.. Looks up a localized string similar to ExpandDirection is a read-only value and is set through Accordion.. Looks up a localized string similar to Cannot modify the IsSelected property while item is locked.. Looks up a localized string similar to Invalid Action '{0}'.. Looks up a localized string similar to Cannot start an action when there is no action scheduled.. Looks up a localized string similar to Cannot perform operation.. Looks up a localized string similar to Invalid ExpandDirection value '{0}'.. Looks up a localized string similar to Transition '{0}' was not defined.. Looks up a localized string similar to IsTransitioning property is read-only.. Represents a control with a single piece of content and when that content changes performs a transition animation. Experimental The API for this control will change considerably in the future. The name of the group that holds the presentation states. The name of the state that represents a normal situation where no transition is currently being used. The name of the state that represents the default transition. The name of the control that will display the previous content. The name of the control that will display the current content. Gets or sets the current content presentation site. The current content presentation site. Gets or sets the previous content presentation site. The previous content presentation site. Indicates whether the control allows writing IsTransitioning. Gets a value indicating whether this instance is currently performing a transition. Identifies the IsTransitioning dependency property. IsTransitioningProperty property changed handler. TransitioningContentControl that changed its IsTransitioning. Event arguments. The storyboard that is used to transition old and new content. Gets or sets the storyboard that is used to transition old and new content. Gets or sets the name of the transition to use. These correspond directly to the VisualStates inside the PresentationStates group. Identifies the Transition dependency property. TransitionProperty property changed handler. TransitioningContentControl that changed its Transition. Event arguments. Gets or sets a value indicating whether the current transition will be aborted when setting new content during a transition. Identifies the RestartTransitionOnContentChange dependency property. RestartTransitionOnContentChangeProperty property changed handler. TransitioningContentControl that changed its RestartTransitionOnContentChange. Event arguments. Called when the RestartTransitionOnContentChangeProperty changes. The old value of RestartTransitionOnContentChange. The new value of RestartTransitionOnContentChange. Occurs when the current transition has completed. Static constructor Initializes a new instance of the class. Builds the visual tree for the TransitioningContentControl control when a new template is applied. Called when the value of the property changes. The old value of the property. The new value of the property. Starts the transition. The old content. The new content. Handles the Completed event of the transition storyboard. The source of the event. The instance containing the event data. Aborts the transition and releases the previous content. Attempts to find a storyboard that matches the newTransition name. The new transition. A storyboard or null, if no storyboard was found. Provides a framework for elements that virtualize their child data collection. This is an abstract class. Identifies the property. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets a value that indicates that this is virtualizing its child collection. Identifies the property. Gets or sets the of the realization pass for this . This is an attached property that the panel sets on each container (generated or direct) to point back to the index of the item. Holds the latest queued realization operation. Returns the that this panel hosts items for. Returns the index to an item that corresponds to the specified, generated . The that corresponds to the item index to be returned. An index to an item that corresponds to the specified if it was generated and hosted by this panel; otherwise, -1. Returns the item that corresponds to the specified, generated . The that corresponds to the item to be returned. An that is the item which corresponds to the specified if it was generated and hosted by this panel; otherwise, null. Returns the corresponding to the item at the given index within the item collection if it has been realized. The index of the desired item. The element corresponding to the item at the given index within the item collection or returns null if the item is not realized. Returns the corresponding to the given item if it has been realized. The item to find the for. A that corresponds to the given item. Returns null if the item does not belong to the item collection, or if a has not been generated for it. Use caution when calling this method as it does a linear search for the item. Consider calling instead. Invalidates the realization state of all items being hosted by this panel. After the invalidation, the panel will have its reality updated, which will occur asynchronously unless subsequently forced by . Ensures that all items being hosted by this panel are properly realized or virtualized. Manages calls to . A custom state object left over from a previous call to if additional processing was needed. A custom state object if additional processing is needed; otherwise, null. When overridden in a derived class, realizes and/or virtualizes items, optionally deferring additional realization. The current items being hosted by this panel. A custom state object left over from a previous call to if additional processing was needed. Implementations may optionally defer additional processing by return a non-null object, which will then be passed to a future call to . Indicates that the property value has changed. The old property value. The new property value. Indicates that the property value has changed. The old property value. The new property value. Maintains event handlers when the items source has changed. The that raised the event. Provides data for the event. Called when the collection that is associated with the for this changes. The that raised the event. Provides data for the event. Realizes an item container for the item with the given index. The index of the item. The child that was created and added to the internal children. Removes an item container for the item with the given index. The index of the item. true if the child had been previously realized and was now removed; otherwise, false. Returns a list which represents a subset of the elements in the source list. This list is used in NotifyCollectionChangedEventArgs because we might be dealing with virtualized lists that raise events for items changing when the items haven't been loaded into memory yet. If the client needs to inspect the item, then they can index into this list and it will retrieve it from the original source, but if they don't need to inspect the item then we spare the cost of the lookup and retrieval. Defines an area within which you can explicitly position an infinite number of child elements by using coordinates that are relative to the area. Identifies the dependency property. Returns a transform applying the and when is set to true. Dependency object whos value is being coerced. The original uncoerced value. A new transform if is set to true; otherwise, . Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets whether to automatically apply a to the canvas. true or false. The default is . The value of this dependency property is true by default, meaning that the property will contain a that scales the canvas and its children automatically. This property can be set to false prevent the automatic transform. This means that children are responsible for changing their appearance when the property changes. Note that this property does not affect the placement of the elements; the children are automatically placed with the top-left corners of their elements at the appropriate positions on the screen, regardless of the value of . Children will usually do this by simply changing their and to become larger or smaller when the property increases or decreases. This is useful when pen widths are important, such as an element surrounded with a with set to 1.0. If is true, then as decreases the shape will be scaled down and the border stroke will become thinner than one pixel, possibly too thin to see even with sub-pixel rendering. This is also true when drawing paths, edges of a graph, or any other element that uses to draw lines and strokes. In these cases setting to false and setting the 's to while binding its and to a factor of will often provide a better effect. Another reason to set this property to false is when elements change their representation or visual state based on the scale (also known as "semantic zoom"). For example, imagine a canvas showing multiple thumbnails of spreadsheets and the relationships between their formulas and values. When is set to 1.0 (the default value), each spreadsheet element might be fully interactive, editable, and showing all rows and columns. When zooming out, and gets small enough that there is not enough room for each spreadsheet to show all of its rows and columns, it may change its representation into a bar chart or pie chart with axis values and a legend instead. When zooming even further out, and gets small enough that there is not enough room for the axis and legend, it may simply remove the axis and legend to make more room for the graphical portion of the chart. Since the children of the canvas can be arbitary rich UIElements, they can dynamically change their representation and be interacted with at all levels of zoom. This is in sharp contrast to multi-scale-image approaches such as Silverlight's Deep Zoom since those scenarios are simply performing linear scale transformations on pre-computed static bitmaps. Identifies the dependency property. Returns a representing the area of the canvas that is currently being displayed. Dependency object whos value is being coerced. The original uncoerced value. A representing the area of the canvas (in canvas coordinates) that is being displayed by this panel. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets a representing the area of the canvas that is currently being displayed by this panel. A representing the area of the canvas that is currently being displayed by this panel. The value of this property is automatically computed based on the , , and of this panel. It is independent (and usually different) from the dependency property. Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets the portion of the canvas (in canvas coordinates) that should be attempted to be displayed by this panel. A specifying the portion of the canvas that should be displayed by this panel, or when unspecified. The default value is . The area of the canvas shown by this panel can be controlled by either setting and , or by setting the , , and properties. When is set to anything other than , the and will be automatically coerced to appropriate values according to the and properties. Note that the mode of is not supported, so unless the aspect ratio of exactly matches the aspect ratio of the actual area displayed will be more or less than . The exact area that is displayed can be determined by the property in this case. Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets a value that specifies how the content of the canvas is displayed when is set. One of the values other than . The default is . Please see the documentation of for a detailed explanation of the effects of this property. The mode of is not supported, so unless the aspect ratio of exactly matches the aspect ratio of the actual area displayed will be more or less than . The exact area that is displayed can be determined by the property in this case. Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets how setting the property can affect the . One of the values. The default is When setting the property, the and properties are automatically coerced to the appropriate values according to the and properties, and any existing values of and will be overridden. However, when the value of is set to anything other than , then the setting of the property can limit the range of the automatically computed value. The exact area that is displayed can be determined by the property in this case. Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Returns a representing top-left point of the canvas (in canvas coordinates) that is currently being displayed after taking into account. Dependency object whos value is being coerced. The original uncoerced value. A representing top-left point of the canvas (in canvas coordinates) that is currently being displayed. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Returns a representing the scale of the content that is currently being displayed after taking , , and into account. Dependency object whos value is being coerced. The original uncoerced value. A representing scale of the content that is currently being displayed. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets the maximum number of elements that will be instantiated on the canvas when is set to . An between 0 and . The default is . When the children of the canvas are being populated through an , visual elements will be instantiated for the first items within the . Identifies the dependency property. Determines whether the value given is a valid value for the dependency property. The potential value for the dependency property. true if the value is a valid value for the property; otherwise, false. Handles the event that occurs when the value of the dependency property has changed. The dependency object on which the dependency property has changed. The event args containing the old and new values of the dependency property. Gets or sets the maximum number of elements that will be realized or virtualized at one time before yielding control back to the dispatcher when is set to . An between 0 and . The default is . By default, the value of this property is which means that all realization and virtualization happens at once, at the time determined by . The default behavior is optimized to realize all elements as quickly as possible, at the expensive of application responsiveness while the realization is happening. Setting the to and decreasing the will make the application feel more responsive but will take longer to realize all items. Performs a one-time initialization of -related metadata. Ensures coersion routines are invoked with their default values. Provides a two-dimensional index of items that can be quickly queried for all items that intersect a given rectangle. When the is hosting items for an , the can implement this interface to greatly speed up virtualization in the canvas. If any of those conditions are not true, then the canvas must realize every item at least once in order to determine its bounds before it can virtualize it, and then once it is virtualized it will have no means of moving spontaneously back into view. Gets the entire extent of the index, which is typically the union of all bounding boxes of all items within the set. This value is used when determining the extent of the scroll bars when the canvas is hosted in a scroll viewer. Gets the set of items that intersect the given rectangle. The area in which any intersecting items are returned. A result set of all items that intersect the given rectangle. The enumerator returned by this method is used lazily and sometimes only partially, meaning it should return quickly without computing the entire result set immediately for best results. Occurs when the value of the property has changed. Occurs when the results of the last query are no longer valid and should be re-queried. Private implementation of when the items source is not one. This class efficiently implements a spatial index by internally using a PriorityQuadTree data structure. Private class that holds an index/bounds pair. A Tuple could have been used instead except that we want Index to be mutable. We use a PriorityQuadTree to implement our spatial index. This is a list of all of the spatial items in the index. Holds the accurate extent of all item bounds in the index. This may be different from _tree.Extent. Holds the last query used in order to know when to raise the event. Occurs when the value of the property has changed. Occurs when the results of the last query are no longer valid and should be re-queried. Get a list of the items that intersect the given bounds. The bounds to test. List of zero or more items that intersect the given bounds, returned in the order given by the priority assigned during Insert. Gets the computed minimum required rectangle to contain all of the items in the index. This property is also settable for efficiency the future extent of the items is known. Gets or sets the bounds for the item with the given . The index of the item. The bounds of the item, or if the bounds are unknown. Items with bounnds of are always returned first from any query. Adds or inserts the given of items at the given . The index at which to insert the items. The number of items to insert. All items are inserted with bounds of , meaning they will be returned from all queries. Removes the given of items at the given . The index at which to remove from. The number of items to remove. Clears and resets the spatial index to hold the given of items. The number of items within the index. Optimizes the spatial index based on the current extent if optimization is warranted. Two-dimentional spatial index of our data items. Private implementation of when the items source does not provide one. Ordered list of realized items based on the order they are returned from the spatial index. Handles the event that occurs when the value of the property has changed. Refreshes our data when the property has changed. Dispatches to specific methods to update the spatial index when the items have changed. Updates our private spatial index when items are added to the item source. The index of the first item that was added. The items that were added. Updates our private spatial index when items are removed from the item source. The old index of the first item that was removed. The items that were removed. Resets and initializes our spatial indices when the items source has changed. Invalidates reality when the last spatial query is no longer valid. The spatial index. The event arguments. Invalidates the extent when the spatial index extent has changed. The spatial index. The event arguments. Performs realization and virtualization in batches based on the . The current items being hosted by this panel. The previous return value of this method. A non-null value if further realization is required; otherwise, null. Realizes and virtualizes items based on the current viewbox. An enumerator which allows this method to continue realization where it left off. Updates the calculated and the and when the size changes. Size information about the render size. Invalidates the arrangement of canvases when their children's positions change. Dependency object whos position has changed. Event arguments related to the change. Gets the applied scale transform if is set to true. Gets the applied translate transform if is set to true. Scales the child elements of a by applying a transform if is true, or by calling otherwise. The new scale of the canvas. Offsets the child elements of a by applying a transform if is true, or by calling otherwise. The new offset of the canvas. Measures the child elements of a in anticipation of arranging them during the pass. An upper limit that should not be exceeded. A that represents the size that is required to arrange child content. Arranges the content of a element. The size that this element should use to arrange its child elements. A that represents the arranged size of this element and its descendants. Returns a clipping geometry that indicates the area that will be clipped if the property is set to true. The available size of the element. A that represents the area that is clipped if is true. Represents the extent of the instantiated UIElements calculated during . Caches the calculated based on the spatial index and arranged children of the canvas until is called. Gets the extent of the populated area of the canvas (in canvas coordinates). This property is also used to determine the range of the scroll bars when the canvas is hosted within a . Re-computes the of items in the canvas and updates the parent scroll viewer if there is one. Gets the current visual coordinates for a given on this . The in canvas coordinates. The current position of the canvas point on the screen relative to the upper-left corner of this . Gets the point on the canvas that is currently represented by the given on the screen. The on the screen relative to the upper-left corner of this . The point on the canvas that corresponds to the given point on the screen. Returns the the point on the canvas at which the mouse cursor is currently located. Exposes Accordion types to UI Automation. Preview Gets the Accordion that owns this AccordionAutomationPeer. The accordion. Initializes a new instance of the class. The Accordion that is associated with this AccordionAutomationPeer. Gets the name of the Accordion that is associated with this AccordionAutomationPeer. This method is called by GetClassName. The name Accordion. Gets the control type for the Accordion that is associated with this AccordionAutomationPeer. This method is called by GetAutomationControlType. List AutomationControlType. Gets the control pattern for the Accordion that is associated with this AccordionAutomationPeer. The desired PatternInterface. The desired AutomationPeer or null. Gets the collection of child elements of the that is associated with this . A collection of AccordionItemAutomationPeer elements, or null if the Accordion that is associated with this AccordionAutomationPeer is empty. Gets a value indicating whether the UI Automation provider allows more than one child element to be selected concurrently. true if multiple selection is allowed; otherwise, false. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Retrieves a UI Automation provider for each child element that is selected. An array of UI Automation providers. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets a value indicating whether the UI Automation provider requires at least one child element to be selected. true if selection is required; otherwise, false. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Exposes a data item to UI Automation. The item to expose The UI automation object associated with the item Exposes AccordionItem types to UI Automation. Preview Gets the AccordionItem that owns this AccordionItemAutomationPeer. Initializes a new instance of the AccordionAutomationPeer class. The item associated with this AutomationPeer The Accordion that is associated with this item. Gets the control type for the AccordionItem that is associated with this AccordionItemAutomationPeer. This method is called by GetAutomationControlType. Custom AutomationControlType. Gets the name of the AccordionItem that is associated with this AccordionItemAutomationPeer. This method is called by GetClassName. The name AccordionItem. Gets the control pattern for the AccordionItem that is associated with this AccordionItemAutomationPeer. The desired PatternInterface. The desired AutomationPeer or null. Gets the state (expanded or collapsed) of the Accordion. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Collapses the AccordionItem. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Expands the AccordionItem. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds the AccordionItem to the collection of selected items. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets a value indicating whether the Accordion is selected. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Removes the current Accordion from the collection of selected items. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Clears selection from currently selected items and then proceeds to select the current Accordion. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets the UI Automation provider that implements ISelectionProvider and acts as the container for the calling object. This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Wraps an . Constructor The to wrap. Provides extension methods for LinkedList. Finds the next node after the given node that contains the specified value. The type of value in the linked list. The linked list. The node after which to search for the value in the linked list, or null to search from the beginning. The value to locate in the linked list. The first node after the given node that contains the specified value, if found; otherwise, null. Finds the previous node before the given node that contains the specified value. The type of value in the linked list. The linked list. The node before which to search for the value in the linked list, or null to search from the end. The value to locate in the linked list. The first node before the given node that contains the specified value, if found; otherwise, null. This class efficiently stores and lazily retrieves arbitrarily sized and positioned objects in a prioritized order in a quad-tree data structure. This can be used to do efficient hit detection or visibility checks on objects in a two dimensional space. The object does not need to implement any special interface because the Rect Bounds of those objects is handled as a separate argument to Insert. Original class written by Chris Lovett. Prioritization and lazy enumeration added by Kael Rowan. Construct new QuadNode to wrap the given node with given bounds. The node with generic type T. The bounds of that node. The priority of that node. The wrapped node. The Rect bounds of the node. The priority of the node. QuadNodes form a linked list in the Quadrant. Inserts this QuadNode into an existing list and returns the new tail of the list. The tail of an existing circular linked list of QuadNodes, or null if this is the first. The (possibly new) tail of the circular linked list after inserting this QuadNode into it. Walk the linked list of QuadNodes and check them against the given bounds. The bounds to test against each node. A lazy list of nodes along with the priority of the next node. Walk the linked list of QuadNodes and check them against the given bounds. The bounds to test against each node. A lazy list of nodes along with the priority of the next node. Walk the linked list and test each node against the given bounds. Bounds to test. Return true if a node in the list intersects the bounds. Walk the linked list and test each node against the given bounds. Bounds to test. Return true if a node in the list is inside the bounds. The quad tree is split up into four Quadrants and objects are stored in the quadrant that contains them and each quadrant is split up into four child Quadrants recurrsively. Objects that overlap more than one quadrant are stored in the nodes list for this Quadrant. Construct new Quadrant with a given bounds all nodes stored inside this quadrant will fit inside this bounds. The bounds of this quadrant Insert the given node. The wrapped node. The bounds of that node. The priority of that node. The recursive depth of this call, to avoid stack overflows. The quadrant that ultimately holds the node. Removes the first occurance of the given node from this quadrant or any child quadrants within the search bounds. The node to remove. The bounds to search within. true if the node was found and removed; otherwise, false. Returns all nodes in this quadrant that intersect the given bounds. The nodes are returned in order of descending priority. The bounds that intersects the nodes you want returned. A lazy list of nodes along with the new potential of this quadrant. Returns all nodes in this quadrant that are fully contained within the given bounds. The nodes are returned in order of descending priority. The bounds that contains the nodes you want returned. A lazy list of nodes along with the new potential of this quadrant. Return true if there are any nodes in this Quadrant are inside the given bounds. The bounds to test true if this quadrant or its subquadrants has nodes inside the bounds; otherwise, false. Return true if there are any nodes in this Quadrant that intersect the given bounds. The bounds to test true if this quadrant or its subquadrants has nodes intersecting the bounds; otherwise, false. Remove the given node from this Quadrant.(non-recursive) The node to remove. Returns true if the node was found and removed. The maximum priority for this quadrant's and all of its subquadrants' nodes. The maximum priority for this quadrant's and all of its subquadrants' nodes. This call assumes that the potential is correctly set on the subquadrants. Enumerates over all nodes within this quadrant in random order. Enumerator that enumerates over all its nodes. The extent defines the subdivisible bounds of the quad tree index. The outer PriorityQuadTree class is essentially just a wrapper around a tree of Quadrants. The MaxTreeDepth limit is required since recursive calls can go that deep if item bounds (height or width) are very small compared to Extent (height or width). The max depth will prevent stack overflow exception in some of the recursive calls we make. With a value of 50 the item bounds can be 2^-50 times the extent before the tree stops growing in height. The extent determines the overall quad-tree indexing strategy. Changing this bounds is expensive since it has to re-divide the entire thing - like a re-hash operation. Insert an item with given bounds and priority into this QuadTree. The item to insert. The bounds of this item. The priority to return this item before others in query results. Gets whether any items are fully inside the given bounds. The bounds to test. true if any items are inside the given bounds; otherwise, false. Get a list of the items that are fully inside the given bounds. The bounds to test. The items that are inside the given bounds, returned in the order given by the priority assigned during Insert. Gets whether any items intersect the given bounds. The bounds to test. true if any items intersect the given bounds; otherwise, false. Get list of nodes that intersect the given bounds. The bounds to test. The items that intersect the given bounds, returned in the order given by the priority assigned during Insert. Removes the first instance of the given item from the tree (if it exists) by searching through the entire tree for the item. The item to remove. true if the item was found and removed; otherwise, false. This overload does a full search through the entire tree for the item. Clients should instead call the overload that takes a if the bounds of the item are known. Removes the first instance of the given item that intersects the given bounds from the tree (if it exists). The item to remove. The bounds within to search for the item. true if the item was found and removed; otherwise, false. This overload does a partial search through the tree, so if the do not intersect the node then the node will be missed. Clients should instead call the overload that does not take a if the bounds of the item are not known. Removes all nodes from the tree. Rebuilds all the Quadrants according to the current QuadTree Bounds. Returns all items in the tree in unspecified order. An enumerator over all items in the tree in random order. To get all items in the tree in prioritized-order then simply call with an infinitely large rectangle. Returns all items in the tree in unspecified order. An enumerator over all items in the tree in random order. To get all items in the tree in prioritized-order then simply call with an infinitely large rectangle. Represents a queue of items that are sorted based on individual priorities. Specifies the type of elements in the queue. Specifies the type of object representing the priority. GeneratedInternalTypeHelper CreateInstance GetPropertyValue SetPropertyValue CreateDelegate AddEventHandler ================================================ FILE: WpfToolkit/Layout/Themes/Generic.xaml ================================================  ================================================ FILE: WpfToolkit/Layout/TransitioningContentControl/System/Windows/Controls/TransitioningContentControl.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media.Animation; using System.Windows.Media; using System.Globalization; namespace System.Windows.Controls { /// /// Represents a control with a single piece of content and when that content /// changes performs a transition animation. /// /// Experimental /// The API for this control will change considerably in the future. [TemplateVisualState(GroupName = PresentationGroup, Name = NormalState)] [TemplateVisualState(GroupName = PresentationGroup, Name = DefaultTransitionState)] [TemplatePart(Name = PreviousContentPresentationSitePartName, Type = typeof(ContentControl))] [TemplatePart(Name = CurrentContentPresentationSitePartName, Type = typeof(ContentControl))] public class TransitioningContentControl : ContentControl { #region Visual state names /// /// The name of the group that holds the presentation states. /// private const string PresentationGroup = "PresentationStates"; /// /// The name of the state that represents a normal situation where no /// transition is currently being used. /// private const string NormalState = "Normal"; /// /// The name of the state that represents the default transition. /// public const string DefaultTransitionState = "DefaultTransition"; #endregion Visual state names #region Template part names /// /// The name of the control that will display the previous content. /// internal const string PreviousContentPresentationSitePartName = "PreviousContentPresentationSite"; /// /// The name of the control that will display the current content. /// internal const string CurrentContentPresentationSitePartName = "CurrentContentPresentationSite"; #endregion Template part names #region TemplateParts /// /// Gets or sets the current content presentation site. /// /// The current content presentation site. private ContentPresenter CurrentContentPresentationSite { get; set; } /// /// Gets or sets the previous content presentation site. /// /// The previous content presentation site. private ContentPresenter PreviousContentPresentationSite { get; set; } #endregion TemplateParts #region public bool IsTransitioning /// /// Indicates whether the control allows writing IsTransitioning. /// private bool _allowIsTransitioningWrite; /// /// Gets a value indicating whether this instance is currently performing /// a transition. /// public bool IsTransitioning { get { return (bool)GetValue(IsTransitioningProperty); } private set { _allowIsTransitioningWrite = true; SetValue(IsTransitioningProperty, value); _allowIsTransitioningWrite = false; } } /// /// Identifies the IsTransitioning dependency property. /// public static readonly DependencyProperty IsTransitioningProperty = DependencyProperty.Register( "IsTransitioning", typeof(bool), typeof(TransitioningContentControl), new PropertyMetadata(OnIsTransitioningPropertyChanged)); /// /// IsTransitioningProperty property changed handler. /// /// TransitioningContentControl that changed its IsTransitioning. /// Event arguments. private static void OnIsTransitioningPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TransitioningContentControl source = (TransitioningContentControl)d; if (!source._allowIsTransitioningWrite) { source.IsTransitioning = (bool)e.OldValue; throw new InvalidOperationException(Properties.Resources.TransitiotioningContentControl_IsTransitioningReadOnly); } } #endregion public bool IsTransitioning /// /// The storyboard that is used to transition old and new content. /// private Storyboard _currentTransition; /// /// Gets or sets the storyboard that is used to transition old and new content. /// private Storyboard CurrentTransition { get { return _currentTransition; } set { // decouple event if (_currentTransition != null) { _currentTransition.Completed -= OnTransitionCompleted; } _currentTransition = value; if (_currentTransition != null) { _currentTransition.Completed += OnTransitionCompleted; } } } #region public string Transition /// /// Gets or sets the name of the transition to use. These correspond /// directly to the VisualStates inside the PresentationStates group. /// public string Transition { get { return GetValue(TransitionProperty) as string; } set { SetValue(TransitionProperty, value); } } /// /// Identifies the Transition dependency property. /// public static readonly DependencyProperty TransitionProperty = DependencyProperty.Register( "Transition", typeof(string), typeof(TransitioningContentControl), new PropertyMetadata(DefaultTransitionState, OnTransitionPropertyChanged)); /// /// TransitionProperty property changed handler. /// /// TransitioningContentControl that changed its Transition. /// Event arguments. private static void OnTransitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TransitioningContentControl source = (TransitioningContentControl)d; string oldTransition = e.NewValue as string; string newTransition = e.NewValue as string; if (source.IsTransitioning) { source.AbortTransition(); } // find new transition Storyboard newStoryboard = source.GetStoryboard(newTransition); // unable to find the transition. if (newStoryboard == null) { // could be during initialization of xaml that presentationgroups was not yet defined if (VisualStates.TryGetVisualStateGroup(source, PresentationGroup) == null) { // will delay check source.CurrentTransition = null; } else { // revert to old value source.SetValue(TransitionProperty, oldTransition); throw new ArgumentException( string.Format(CultureInfo.CurrentCulture, Properties.Resources.TransitioningContentControl_TransitionNotFound, newTransition)); } } else { source.CurrentTransition = newStoryboard; } } #endregion public string Transition #region public bool RestartTransitionOnContentChange /// /// Gets or sets a value indicating whether the current transition /// will be aborted when setting new content during a transition. /// public bool RestartTransitionOnContentChange { get { return (bool)GetValue(RestartTransitionOnContentChangeProperty); } set { SetValue(RestartTransitionOnContentChangeProperty, value); } } /// /// Identifies the RestartTransitionOnContentChange dependency property. /// public static readonly DependencyProperty RestartTransitionOnContentChangeProperty = DependencyProperty.Register( "RestartTransitionOnContentChange", typeof(bool), typeof(TransitioningContentControl), new PropertyMetadata(false, OnRestartTransitionOnContentChangePropertyChanged)); /// /// RestartTransitionOnContentChangeProperty property changed handler. /// /// TransitioningContentControl that changed its RestartTransitionOnContentChange. /// Event arguments. private static void OnRestartTransitionOnContentChangePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TransitioningContentControl) d).OnRestartTransitionOnContentChangeChanged((bool) e.OldValue, (bool) e.NewValue); } /// /// Called when the RestartTransitionOnContentChangeProperty changes. /// /// The old value of RestartTransitionOnContentChange. /// The new value of RestartTransitionOnContentChange. protected virtual void OnRestartTransitionOnContentChangeChanged(bool oldValue, bool newValue) { } #endregion public bool RestartTransitionOnContentChange #region Events /// /// Occurs when the current transition has completed. /// public event RoutedEventHandler TransitionCompleted; #endregion Events #if !SILVERLIGHT /// /// Static constructor /// static TransitioningContentControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(TransitioningContentControl), new FrameworkPropertyMetadata(typeof(TransitioningContentControl))); } #endif /// /// Initializes a new instance of the class. /// public TransitioningContentControl() { #if SILVERLIGHT DefaultStyleKey = typeof(TransitioningContentControl); #endif } /// /// Builds the visual tree for the TransitioningContentControl control /// when a new template is applied. /// public override void OnApplyTemplate() { if (IsTransitioning) { AbortTransition(); } base.OnApplyTemplate(); PreviousContentPresentationSite = GetTemplateChild(PreviousContentPresentationSitePartName) as ContentPresenter; CurrentContentPresentationSite = GetTemplateChild(CurrentContentPresentationSitePartName) as ContentPresenter; if (CurrentContentPresentationSite != null) { CurrentContentPresentationSite.Content = Content; } // hookup currenttransition Storyboard transition = GetStoryboard(Transition); CurrentTransition = transition; if (transition == null) { string invalidTransition = Transition; // revert to default Transition = DefaultTransitionState; throw new ArgumentException( string.Format(CultureInfo.CurrentCulture, Properties.Resources.TransitioningContentControl_TransitionNotFound, invalidTransition)); } VisualStateManager.GoToState(this, NormalState, false); } /// /// Called when the value of the property changes. /// /// The old value of the property. /// The new value of the property. protected override void OnContentChanged(object oldContent, object newContent) { base.OnContentChanged(oldContent, newContent); StartTransition(oldContent, newContent); } /// /// Starts the transition. /// /// The old content. /// The new content. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newContent", Justification = "Should be used in the future.")] private void StartTransition(object oldContent, object newContent) { // both presenters must be available, otherwise a transition is useless. if (CurrentContentPresentationSite != null && PreviousContentPresentationSite != null) { CurrentContentPresentationSite.Content = newContent; PreviousContentPresentationSite.Content = oldContent; // and start a new transition if (!IsTransitioning || RestartTransitionOnContentChange) { IsTransitioning = true; VisualStateManager.GoToState(this, NormalState, false); VisualStateManager.GoToState(this, Transition, true); } } } /// /// Handles the Completed event of the transition storyboard. /// /// The source of the event. /// The instance containing the event data. private void OnTransitionCompleted(object sender, EventArgs e) { AbortTransition(); RoutedEventHandler handler = TransitionCompleted; if (handler != null) { handler(this, new RoutedEventArgs()); } } /// /// Aborts the transition and releases the previous content. /// public void AbortTransition() { // go to normal state and release our hold on the old content. VisualStateManager.GoToState(this, NormalState, false); IsTransitioning = false; if (PreviousContentPresentationSite != null) { PreviousContentPresentationSite.Content = null; } } /// /// Attempts to find a storyboard that matches the newTransition name. /// /// The new transition. /// A storyboard or null, if no storyboard was found. private Storyboard GetStoryboard(string newTransition) { VisualStateGroup presentationGroup = VisualStates.TryGetVisualStateGroup(this, PresentationGroup); Storyboard newStoryboard = null; if (presentationGroup != null) { newStoryboard = presentationGroup.States .OfType() .Where(state => state.Name == newTransition) .Select(state => state.Storyboard) .FirstOrDefault(); } return newStoryboard; } } } ================================================ FILE: WpfToolkit/Layout/ZoomableCanvas/LinkedListExtensions.cs ================================================ namespace System.Collections.Generic { /// /// Provides extension methods for LinkedList. /// public static class LinkedListExtensions { /// /// Finds the next node after the given node that contains the specified value. /// /// The type of value in the linked list. /// The linked list. /// The node after which to search for the value in the linked list, or null to search from the beginning. /// The value to locate in the linked list. /// The first node after the given node that contains the specified value, if found; otherwise, null. public static LinkedListNode FindNext(this LinkedList list, LinkedListNode node, T value) { if (list == null) { throw new ArgumentNullException("list"); } if (node == null) { return list.Find(value); } if (list != node.List) { throw new ArgumentException("The list does not contain the given node."); } EqualityComparer comparer = EqualityComparer.Default; // Skip the given node. node = node.Next; while (node != null) { if (value != null) { if (comparer.Equals(node.Value, value)) { return node; } } else if (node.Value == null) { return node; } node = node.Next; } return null; } /// /// Finds the previous node before the given node that contains the specified value. /// /// The type of value in the linked list. /// The linked list. /// The node before which to search for the value in the linked list, or null to search from the end. /// The value to locate in the linked list. /// The first node before the given node that contains the specified value, if found; otherwise, null. public static LinkedListNode FindPrevious(this LinkedList list, LinkedListNode node, T value) { if (list == null) { throw new ArgumentNullException("list"); } if (node == null) { return list.FindLast(value); } if (list != node.List) { throw new ArgumentException("The list does not contain the given node."); } EqualityComparer comparer = EqualityComparer.Default; // Skip the given node. node = node.Previous; while (node != null) { if (value != null) { if (comparer.Equals(node.Value, value)) { return node; } } else if (node.Value == null) { return node; } node = node.Previous; } return null; } } } ================================================ FILE: WpfToolkit/Layout/ZoomableCanvas/PriorityQuadTree.cs ================================================ using System; using System.Linq; using System.Windows; using System.Windows.Controls.Extensions; namespace System.Collections.Generic { /// /// This class efficiently stores and lazily retrieves arbitrarily sized and positioned objects in a prioritized order in a quad-tree data structure. /// This can be used to do efficient hit detection or visibility checks on objects in a two dimensional space. /// The object does not need to implement any special interface because the Rect Bounds of those objects is handled as a separate argument to Insert. /// /// /// Original class written by Chris Lovett. /// Prioritization and lazy enumeration added by Kael Rowan. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] public class PriorityQuadTree : IEnumerable { /// /// Each node stored in the tree has a position, width & height. /// private class QuadNode { private readonly Rect _bounds; // the bounds of the node private readonly T _node; // the actual object being stored here. private readonly double _priority; // the priority of the object being stored here. private QuadNode _next; // linked in a circular list. /// /// Construct new QuadNode to wrap the given node with given bounds. /// /// The node with generic type T. /// The bounds of that node. /// The priority of that node. public QuadNode(T node, Rect bounds, double priority) { _node = node; _bounds = bounds; _priority = priority; } /// /// The wrapped node. /// public T Node { get { return _node; } } /// /// The Rect bounds of the node. /// public Rect Bounds { get { return _bounds; } } /// /// The priority of the node. /// public double Priority { get { return _priority; } } /// /// QuadNodes form a linked list in the Quadrant. /// public QuadNode Next { get { return _next; } set { _next = value; } } /// /// Inserts this QuadNode into an existing list and returns the new tail of the list. /// /// The tail of an existing circular linked list of QuadNodes, or null if this is the first. /// The (possibly new) tail of the circular linked list after inserting this QuadNode into it. public QuadNode InsertInto(QuadNode tail) { if (tail == null) { Next = this; tail = this; } else { // link up in circular link list. if (Priority < tail.Priority) { Next = tail.Next; tail.Next = this; tail = this; } else { QuadNode x; for (x = tail; x.Next != tail && Priority < x.Next.Priority; x = x.Next) ; Next = x.Next; x.Next = this; } } return tail; } /// /// Walk the linked list of QuadNodes and check them against the given bounds. /// /// The bounds to test against each node. /// A lazy list of nodes along with the priority of the next node. public IEnumerable> GetNodesIntersecting(Rect bounds) { QuadNode n = this; do { n = n.Next; // first node. if (bounds.Intersects(n.Bounds)) { yield return Tuple.Create(n, n != this ? n.Next.Priority : double.NaN); } } while (n != this); } /// /// Walk the linked list of QuadNodes and check them against the given bounds. /// /// The bounds to test against each node. /// A lazy list of nodes along with the priority of the next node. public IEnumerable> GetNodesInside(Rect bounds) { QuadNode n = this; do { n = n.Next; // first node. if (bounds.Contains(n.Bounds)) { yield return Tuple.Create(n, n != this ? n.Next.Priority : double.NaN); } } while (n != this); } /// /// Walk the linked list and test each node against the given bounds. /// /// Bounds to test. /// Return true if a node in the list intersects the bounds. public bool HasNodesIntersecting(Rect bounds) { QuadNode n = this; do { n = n.Next; // first node. if (bounds.Intersects(n.Bounds)) { return true; } } while (n != this); return false; } /// /// Walk the linked list and test each node against the given bounds. /// /// Bounds to test. /// Return true if a node in the list is inside the bounds. public bool HasNodesInside(Rect bounds) { QuadNode n = this; do { n = n.Next; // first node. if (bounds.Contains(n.Bounds)) { return true; } } while (n != this); return false; } } /// /// The quad tree is split up into four Quadrants and objects are stored in the quadrant that contains them /// and each quadrant is split up into four child Quadrants recurrsively. Objects that overlap more than /// one quadrant are stored in the nodes list for this Quadrant. /// private class Quadrant : IEnumerable { private readonly Rect _bounds; // quadrant bounds. private double _potential = Double.NegativeInfinity; // the maximum priority of all nodes within this quadrant. private int _count; private QuadNode _nodes; // nodes that overlap the sub quadrant boundaries. // The quadrant is subdivided when nodes are inserted that are // completely contained within those subdivisions. private Quadrant _topLeft; private Quadrant _topRight; private Quadrant _bottomLeft; private Quadrant _bottomRight; /// /// Construct new Quadrant with a given bounds all nodes stored inside this quadrant /// will fit inside this bounds. /// /// The bounds of this quadrant public Quadrant(Rect bounds) { _bounds = bounds; } /// /// Insert the given node. /// /// The wrapped node. /// The bounds of that node. /// The priority of that node. /// The recursive depth of this call, to avoid stack overflows. /// The quadrant that ultimately holds the node. internal Quadrant Insert(T node, Rect bounds, double priority, int depth) { _potential = Math.Max(_potential, priority); _count++; Quadrant child = null; // Only drill down the tree for positive sized bounds, otherwise we could drill forever. // Todo: We can remove this restriction if we choose to only split quads when "full". if (depth <= PriorityQuadTree.MaxTreeDepth && (bounds.Width > 0 || bounds.Height > 0)) { double w = _bounds.Width / 2; double h = _bounds.Height / 2; // assumption that the Rect struct is almost as fast as doing the operations // manually since Rect is a value type. Rect topLeft = new Rect(_bounds.Left, _bounds.Top, w, h); Rect topRight = new Rect(_bounds.Left + w, _bounds.Top, w, h); Rect bottomLeft = new Rect(_bounds.Left, _bounds.Top + h, w, h); Rect bottomRight = new Rect(_bounds.Left + w, _bounds.Top + h, w, h); // See if any child quadrants completely contain this node. if (topLeft.Contains(bounds)) { if (_topLeft == null) { _topLeft = new Quadrant(topLeft); } child = _topLeft; } else if (topRight.Contains(bounds)) { if (_topRight == null) { _topRight = new Quadrant(topRight); } child = _topRight; } else if (bottomLeft.Contains(bounds)) { if (_bottomLeft == null) { _bottomLeft = new Quadrant(bottomLeft); } child = _bottomLeft; } else if (bottomRight.Contains(bounds)) { if (_bottomRight == null) { _bottomRight = new Quadrant(bottomRight); } child = _bottomRight; } } if (child != null) { return child.Insert(node, bounds, priority, depth + 1); } else { QuadNode n = new QuadNode(node, bounds, priority); _nodes = n.InsertInto(_nodes); return this; } } /// /// Removes the first occurance of the given node from this quadrant or any child quadrants within the search bounds. /// /// The node to remove. /// The bounds to search within. /// true if the node was found and removed; otherwise, false. internal bool Remove(T node, Rect bounds) { bool nodeRemoved = false; if (RemoveNode(node)) { nodeRemoved = true; } else { double w = _bounds.Width / 2; double h = _bounds.Height / 2; // assumption that the Rect struct is almost as fast as doing the operations // manually since Rect is a value type. Rect topLeft = new Rect(_bounds.Left, _bounds.Top, w, h); Rect topRight = new Rect(_bounds.Left + w, _bounds.Top, w, h); Rect bottomLeft = new Rect(_bounds.Left, _bounds.Top + h, w, h); Rect bottomRight = new Rect(_bounds.Left + w, _bounds.Top + h, w, h); if (_topLeft != null && topLeft.Intersects(bounds) && _topLeft.Remove(node, bounds)) { if (_topLeft._count == 0) { _topLeft = null; } nodeRemoved = true; } else if (_topRight != null && topRight.Intersects(bounds) && _topRight.Remove(node, bounds)) { if (_topRight._count == 0) { _topRight = null; } nodeRemoved = true; } else if (_bottomLeft != null && bottomLeft.Intersects(bounds) && _bottomLeft.Remove(node, bounds)) { if (_bottomLeft._count == 0) { _bottomLeft = null; } nodeRemoved = true; } else if (_bottomRight != null && bottomRight.Intersects(bounds) && _bottomRight.Remove(node, bounds)) { if (_bottomRight._count == 0) { _bottomRight = null; } nodeRemoved = true; } } if (nodeRemoved) { _count--; _potential = CalculatePotential(); return true; } return false; } /// /// Returns all nodes in this quadrant that intersect the given bounds. /// The nodes are returned in order of descending priority. /// /// The bounds that intersects the nodes you want returned. /// A lazy list of nodes along with the new potential of this quadrant. internal IEnumerable> GetNodesIntersecting(Rect bounds) { double w = _bounds.Width / 2; double h = _bounds.Height / 2; // assumption that the Rect struct is almost as fast as doing the operations // manually since Rect is a value type. Rect topLeft = new Rect(_bounds.Left, _bounds.Top, w, h); Rect topRight = new Rect(_bounds.Left + w, _bounds.Top, w, h); Rect bottomLeft = new Rect(_bounds.Left, _bounds.Top + h, w, h); Rect bottomRight = new Rect(_bounds.Left + w, _bounds.Top + h, w, h); // Create a priority queue based on the potential of our nodes and our quads. var queue = new PriorityQueue>, double>(true); if (_nodes != null) { queue.Enqueue(_nodes.GetNodesIntersecting(bounds).GetEnumerator(), _nodes.Next.Priority); } if (_topLeft != null && topLeft.Intersects(bounds)) { queue.Enqueue(_topLeft.GetNodesIntersecting(bounds).GetEnumerator(), _topLeft._potential); } if (_topRight != null && topRight.Intersects(bounds)) { queue.Enqueue(_topRight.GetNodesIntersecting(bounds).GetEnumerator(), _topRight._potential); } if (_bottomLeft != null && bottomLeft.Intersects(bounds)) { queue.Enqueue(_bottomLeft.GetNodesIntersecting(bounds).GetEnumerator(), _bottomLeft._potential); } if (_bottomRight != null && bottomRight.Intersects(bounds)) { queue.Enqueue(_bottomRight.GetNodesIntersecting(bounds).GetEnumerator(), _bottomRight._potential); } // Then just loop through the queue. while (queue.Count > 0) { // Grab the enumerator with the highest potential. var enumerator = queue.Dequeue().Key; if (enumerator.MoveNext()) { // Get the current node and its new potential from the enumerator. var current = enumerator.Current; var node = current.Item1; var potential = current.Item2; // Determine our new potential. var newPotential = queue.Count > 0 ? !potential.IsNaN() ? Math.Max(potential, queue.Peek().Value) : queue.Peek().Value : potential; // It might be the case that the actual intersecting node has less priority than our remaining potential. if (newPotential > node.Priority) { // Store it for later in a container containing only it with no further potential. var store = Enumerable.Repeat(Tuple.Create(node, double.NaN), 1).GetEnumerator(); // Enqueue the container at the correct position. queue.Enqueue(store, node.Priority); } else { // Return it to our parent along with our new potential. yield return Tuple.Create(node, newPotential); } // If this enumerator has some more potential then re-enqueue it. if (!potential.IsNaN()) { queue.Enqueue(enumerator, potential); } } } } /// /// Returns all nodes in this quadrant that are fully contained within the given bounds. /// The nodes are returned in order of descending priority. /// /// The bounds that contains the nodes you want returned. /// A lazy list of nodes along with the new potential of this quadrant. internal IEnumerable> GetNodesInside(Rect bounds) { double w = _bounds.Width / 2; double h = _bounds.Height / 2; // assumption that the Rect struct is almost as fast as doing the operations // manually since Rect is a value type. Rect topLeft = new Rect(_bounds.Left, _bounds.Top, w, h); Rect topRight = new Rect(_bounds.Left + w, _bounds.Top, w, h); Rect bottomLeft = new Rect(_bounds.Left, _bounds.Top + h, w, h); Rect bottomRight = new Rect(_bounds.Left + w, _bounds.Top + h, w, h); // Create a priority queue based on the potential of our nodes and our quads. var queue = new PriorityQueue>, double>(true); if (_nodes != null) { queue.Enqueue(_nodes.GetNodesInside(bounds).GetEnumerator(), _nodes.Next.Priority); } if (_topLeft != null && topLeft.Intersects(bounds)) { queue.Enqueue(_topLeft.GetNodesInside(bounds).GetEnumerator(), _topLeft._potential); } if (_topRight != null && topRight.Intersects(bounds)) { queue.Enqueue(_topRight.GetNodesInside(bounds).GetEnumerator(), _topRight._potential); } if (_bottomLeft != null && bottomLeft.Intersects(bounds)) { queue.Enqueue(_bottomLeft.GetNodesInside(bounds).GetEnumerator(), _bottomLeft._potential); } if (_bottomRight != null && bottomRight.Intersects(bounds)) { queue.Enqueue(_bottomRight.GetNodesInside(bounds).GetEnumerator(), _bottomRight._potential); } // Then just loop through the queue. while (queue.Count > 0) { // Grab the enumerator with the highest potential. var enumerator = queue.Dequeue().Key; if (enumerator.MoveNext()) { // Get the current node and its new potential from the enumerator. var current = enumerator.Current; var node = current.Item1; var potential = current.Item2; // Determine our new potential. var newPotential = queue.Count > 0 ? !potential.IsNaN() ? Math.Max(potential, queue.Peek().Value) : queue.Peek().Value : potential; // It might be the case that the actual intersecting node has less priority than our remaining potential. if (newPotential > node.Priority) { // Store it for later in a container containing only it with no further potential. var store = Enumerable.Repeat(Tuple.Create(node, double.NaN), 1).GetEnumerator(); // Enqueue the container at the correct position. queue.Enqueue(store, node.Priority); } else { // Return it to our parent along with our new potential. yield return Tuple.Create(node, newPotential); } // If this enumerator has some more potential then re-enqueue it. if (!potential.IsNaN()) { queue.Enqueue(enumerator, potential); } } } } /// /// Return true if there are any nodes in this Quadrant are inside the given bounds. /// /// The bounds to test /// true if this quadrant or its subquadrants has nodes inside the bounds; otherwise, false. internal bool HasNodesInside(Rect bounds) { double w = _bounds.Width / 2; double h = _bounds.Height / 2; // assumption that the Rect struct is almost as fast as doing the operations // manually since Rect is a value type. Rect topLeft = new Rect(_bounds.Left, _bounds.Top, w, h); Rect topRight = new Rect(_bounds.Left + w, _bounds.Top, w, h); Rect bottomLeft = new Rect(_bounds.Left, _bounds.Top + h, w, h); Rect bottomRight = new Rect(_bounds.Left + w, _bounds.Top + h, w, h); if (_nodes != null && _nodes.HasNodesInside(bounds)) { return true; } if (_topLeft != null && topLeft.Contains(bounds) && _topLeft.HasNodesInside(bounds)) { return true; } if (_topRight != null && topRight.Contains(bounds) && _topRight.HasNodesInside(bounds)) { return true; } if (_bottomLeft != null && bottomLeft.Contains(bounds) && _bottomLeft.HasNodesInside(bounds)) { return true; } if (_bottomRight != null && bottomRight.Contains(bounds) && _bottomRight.HasNodesInside(bounds)) { return true; } return false; } /// /// Return true if there are any nodes in this Quadrant that intersect the given bounds. /// /// The bounds to test /// true if this quadrant or its subquadrants has nodes intersecting the bounds; otherwise, false. internal bool HasNodesIntersecting(Rect bounds) { double w = _bounds.Width / 2; double h = _bounds.Height / 2; // assumption that the Rect struct is almost as fast as doing the operations // manually since Rect is a value type. Rect topLeft = new Rect(_bounds.Left, _bounds.Top, w, h); Rect topRight = new Rect(_bounds.Left + w, _bounds.Top, w, h); Rect bottomLeft = new Rect(_bounds.Left, _bounds.Top + h, w, h); Rect bottomRight = new Rect(_bounds.Left + w, _bounds.Top + h, w, h); if (_nodes != null && _nodes.HasNodesIntersecting(bounds)) { return true; } if (_topLeft != null && topLeft.Intersects(bounds) && _topLeft.HasNodesIntersecting(bounds)) { return true; } if (_topRight != null && topRight.Intersects(bounds) && _topRight.HasNodesIntersecting(bounds)) { return true; } if (_bottomLeft != null && bottomLeft.Intersects(bounds) && _bottomLeft.HasNodesIntersecting(bounds)) { return true; } if (_bottomRight != null && bottomRight.Intersects(bounds) && _bottomRight.HasNodesIntersecting(bounds)) { return true; } return false; } /// /// Remove the given node from this Quadrant.(non-recursive) /// /// The node to remove. /// Returns true if the node was found and removed. private bool RemoveNode(T node) { bool rc = false; if (_nodes != null) { QuadNode p = _nodes; while (!Object.Equals(p.Next.Node, node) && p.Next != _nodes) { p = p.Next; } if (Object.Equals(p.Next.Node, node)) { rc = true; QuadNode n = p.Next; if (p == n) { // list goes to empty _nodes = null; } else { if (_nodes == n) { _nodes = p; } p.Next = n.Next; } } } return rc; } /// /// The maximum priority for this quadrant's and all of its subquadrants' nodes. /// /// The maximum priority for this quadrant's and all of its subquadrants' nodes. /// This call assumes that the potential is correctly set on the subquadrants. private double CalculatePotential() { double potential = Double.NegativeInfinity; if (_nodes != null) { potential = _nodes.Next.Priority; } if (_topLeft != null) { potential = Math.Max(potential, _topLeft._potential); } if (_topRight != null) { potential = Math.Max(potential, _topRight._potential); } if (_bottomLeft != null) { potential = Math.Max(potential, _bottomLeft._potential); } if (_bottomRight != null) { potential = Math.Max(potential, _bottomRight._potential); } return potential; } /// /// Enumerates over all nodes within this quadrant in random order. /// /// /// Enumerator that enumerates over all its nodes. /// public IEnumerator GetEnumerator() { var queue = new Queue(); queue.Enqueue(this); while (queue.Count > 0) { var quadrant = queue.Dequeue(); if (quadrant._nodes != null) { var n = quadrant._nodes; do { n = n.Next; yield return n; } while (n != quadrant._nodes); } if (quadrant._topLeft != null) { queue.Enqueue(quadrant._topLeft); } if (quadrant._topRight != null) { queue.Enqueue(quadrant._topRight); } if (quadrant._bottomLeft != null) { queue.Enqueue(quadrant._bottomLeft); } if (quadrant._bottomRight != null) { queue.Enqueue(quadrant._bottomRight); } } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } /// /// The extent defines the subdivisible bounds of the quad tree index. /// private Rect _extent; /// /// The outer PriorityQuadTree class is essentially just a wrapper around a tree of Quadrants. /// private Quadrant _root; /// /// The MaxTreeDepth limit is required since recursive calls can go that deep if item bounds (height or width) are very small compared to Extent (height or width). /// The max depth will prevent stack overflow exception in some of the recursive calls we make. /// With a value of 50 the item bounds can be 2^-50 times the extent before the tree stops growing in height. /// private const int MaxTreeDepth = 50; /// /// The extent determines the overall quad-tree indexing strategy. /// Changing this bounds is expensive since it has to re-divide the entire thing - like a re-hash operation. /// public Rect Extent { get { return _extent; } set { if (!(value.Top >= double.MinValue && value.Top <= double.MaxValue && value.Left >= double.MinValue && value.Left <= double.MaxValue && value.Width <= double.MaxValue && value.Height <= double.MaxValue)) { throw new ArgumentOutOfRangeException("value"); } _extent = value; ReIndex(); } } /// /// Insert an item with given bounds and priority into this QuadTree. /// /// The item to insert. /// The bounds of this item. /// The priority to return this item before others in query results. public void Insert(T item, Rect bounds, double priority) { if (bounds.Top.IsNaN() || bounds.Left.IsNaN() || bounds.Width.IsNaN() || bounds.Height.IsNaN()) { throw new ArgumentOutOfRangeException("bounds"); } if (_root == null) { _root = new Quadrant(_extent); } if (Double.IsNaN(priority)) { priority = Double.NegativeInfinity; } _root.Insert(item, bounds, priority, 1); } /// /// Gets whether any items are fully inside the given bounds. /// /// The bounds to test. /// true if any items are inside the given bounds; otherwise, false. public bool HasItemsInside(Rect bounds) { if (bounds.Top.IsNaN() || bounds.Left.IsNaN() || bounds.Width.IsNaN() || bounds.Height.IsNaN()) { throw new ArgumentOutOfRangeException("bounds"); } if (_root != null) { return _root.HasNodesInside(bounds); } return false; } /// /// Get a list of the items that are fully inside the given bounds. /// /// The bounds to test. /// The items that are inside the given bounds, returned in the order given by the priority assigned during Insert. public IEnumerable GetItemsInside(Rect bounds) { if (bounds.Top.IsNaN() || bounds.Left.IsNaN() || bounds.Width.IsNaN() || bounds.Height.IsNaN()) { throw new ArgumentOutOfRangeException(); } if (_root != null) { foreach (var node in _root.GetNodesInside(bounds)) { yield return node.Item1.Node; } } } /// /// Gets whether any items intersect the given bounds. /// /// The bounds to test. /// true if any items intersect the given bounds; otherwise, false. public bool HasItemsIntersecting(Rect bounds) { if (bounds.Top.IsNaN() || bounds.Left.IsNaN() || bounds.Width.IsNaN() || bounds.Height.IsNaN()) { throw new ArgumentOutOfRangeException("bounds"); } if (_root != null) { return _root.HasNodesIntersecting(bounds); } return false; } /// /// Get list of nodes that intersect the given bounds. /// /// The bounds to test. /// The items that intersect the given bounds, returned in the order given by the priority assigned during Insert. public IEnumerable GetItemsIntersecting(Rect bounds) { if (bounds.Top.IsNaN() || bounds.Left.IsNaN() || bounds.Width.IsNaN() || bounds.Height.IsNaN()) { throw new ArgumentOutOfRangeException(); } if (_root != null) { foreach (var node in _root.GetNodesIntersecting(bounds)) { yield return node.Item1.Node; } } } /// /// Removes the first instance of the given item from the tree (if it exists) by searching through the entire tree for the item. /// /// The item to remove. /// true if the item was found and removed; otherwise, false. /// /// This overload does a full search through the entire tree for the item. /// Clients should instead call the overload that takes a if the bounds of the item are known. /// public bool Remove(T item) { return Remove(item, new Rect(double.NegativeInfinity, double.NegativeInfinity, double.PositiveInfinity, double.PositiveInfinity)); } /// /// Removes the first instance of the given item that intersects the given bounds from the tree (if it exists). /// /// The item to remove. /// The bounds within to search for the item. /// true if the item was found and removed; otherwise, false. /// /// This overload does a partial search through the tree, so if the do not intersect the node then the node will be missed. /// Clients should instead call the overload that does not take a if the bounds of the item are not known. /// public bool Remove(T item, Rect bounds) { if (bounds.Top.IsNaN() || bounds.Left.IsNaN() || bounds.Width.IsNaN() || bounds.Height.IsNaN()) { throw new ArgumentOutOfRangeException("bounds"); } if (_root != null) { return _root.Remove(item, bounds); } return false; } /// /// Removes all nodes from the tree. /// public void Clear() { _root = null; } /// /// Rebuilds all the Quadrants according to the current QuadTree Bounds. /// private void ReIndex() { Quadrant quadrant = _root; _root = new Quadrant(_extent); if (quadrant != null) { foreach (var node in quadrant.GetNodesIntersecting(_extent)) { // Todo: It would be more efficient if we added a code path that allowed reuse of the QuadNode wrappers. Insert(node.Item1.Node, node.Item1.Bounds, node.Item1.Priority); } } } /// /// Returns all items in the tree in unspecified order. /// /// An enumerator over all items in the tree in random order. /// To get all items in the tree in prioritized-order then simply call with an infinitely large rectangle. public IEnumerator GetEnumerator() { if (_root != null) { foreach (var node in _root) { yield return node.Node; } } } /// /// Returns all items in the tree in unspecified order. /// /// An enumerator over all items in the tree in random order. /// To get all items in the tree in prioritized-order then simply call with an infinitely large rectangle. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: WpfToolkit/Layout/ZoomableCanvas/PriorityQueue.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; namespace System.Collections.Generic { /// /// Represents a queue of items that are sorted based on individual priorities. /// /// Specifies the type of elements in the queue. /// Specifies the type of object representing the priority. [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] public class PriorityQueue { private readonly List> heap = new List>(); private readonly Dictionary indexes = new Dictionary(); private readonly IComparer comparer; private readonly bool invert; public PriorityQueue() : this(false) { } public PriorityQueue(bool invert) : this(Comparer.Default) { this.invert = invert; } public PriorityQueue(IComparer comparer) { this.comparer = comparer; heap.Add(default(KeyValuePair)); } public void Enqueue(T item, TPriority priority) { KeyValuePair tail = new KeyValuePair(item, priority); heap.Add(tail); MoveUp(tail, Count); } public KeyValuePair Dequeue() { int bound = Count; if (bound < 1) throw new InvalidOperationException("Queue is empty."); KeyValuePair head = heap[1]; KeyValuePair tail = heap[bound]; heap.RemoveAt(bound); if (bound > 1) MoveDown(tail, 1); indexes.Remove(head.Key); return head; } public KeyValuePair Peek() { if (Count < 1) throw new InvalidOperationException("Queue is empty."); return heap[1]; } public bool TryGetValue(T item, out TPriority priority) { int index; if (indexes.TryGetValue(item, out index)) { priority = heap[indexes[item]].Value; return true; } else { priority = default(TPriority); return false; } } public TPriority this[T item] { get { return heap[indexes[item]].Value; } set { int index; if (indexes.TryGetValue(item, out index)) { int order = comparer.Compare(value, heap[index].Value); if (order != 0) { if (invert) order = ~order; KeyValuePair element = new KeyValuePair(item, value); if (order < 0) MoveUp(element, index); else MoveDown(element, index); } } else { KeyValuePair element = new KeyValuePair(item, value); heap.Add(element); MoveUp(element, Count); } } } public int Count { get { return heap.Count - 1; } } private void MoveUp(KeyValuePair element, int index) { while (index > 1) { int parent = index >> 1; if (IsPrior(heap[parent], element)) break; heap[index] = heap[parent]; indexes[heap[parent].Key] = index; index = parent; } heap[index] = element; indexes[element.Key] = index; } private void MoveDown(KeyValuePair element, int index) { int count = heap.Count; while (index << 1 < count) { int child = index << 1; int sibling = child | 1; if (sibling < count && IsPrior(heap[sibling], heap[child])) child = sibling; if (IsPrior(element, heap[child])) break; heap[index] = heap[child]; indexes[heap[child].Key] = index; index = child; } heap[index] = element; indexes[element.Key] = index; } private bool IsPrior(KeyValuePair element1, KeyValuePair element2) { int order = comparer.Compare(element1.Value, element2.Value); if (invert) order = ~order; return order < 0; } } } ================================================ FILE: WpfToolkit/Layout/ZoomableCanvas/VirtualPanel.cs ================================================ using System; using System.Collections; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Windows.Controls.Primitives; using System.Windows.Threading; namespace System.Windows.Controls { /// /// Provides a framework for elements that virtualize their child data collection. This is an abstract class. /// public abstract class VirtualPanel : VirtualizingPanel { /// /// Identifies the property. /// public static readonly DependencyProperty IsVirtualizingProperty = VirtualizingStackPanel.IsVirtualizingProperty.AddOwner(typeof(VirtualPanel), new FrameworkPropertyMetadata(VirtualizingStackPanel.IsVirtualizingProperty.DefaultMetadata.DefaultValue, OnIsVirtualizingChanged)); /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnIsVirtualizingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var panel = d as VirtualPanel; if (panel != null) { panel.OnIsVirtualizingChanged((bool)e.OldValue, (bool)e.NewValue); } } /// /// Gets or sets a value that indicates that this is virtualizing its child collection. /// public bool IsVirtualizing { get { return (bool)GetValue(IsVirtualizingProperty); } set { SetValue(IsVirtualizingProperty, value); } } /// /// Identifies the property. /// public static readonly DependencyProperty RealizationPriorityProperty = DependencyProperty.Register("RealizationPriority", typeof(DispatcherPriority), typeof(VirtualPanel), new FrameworkPropertyMetadata(DispatcherPriority.Normal)); /// /// Gets or sets the of the realization pass for this . /// public DispatcherPriority RealizationPriority { get { return (DispatcherPriority)GetValue(RealizationPriorityProperty); } set { SetValue(RealizationPriorityProperty, value); } } /// /// This is an attached property that the panel sets on each container (generated or direct) to point back to the index of the item. /// private static readonly DependencyProperty IndexForItemContainerProperty = DependencyProperty.RegisterAttached("IndexForItemContainer", typeof(int), typeof(VirtualPanel), new FrameworkPropertyMetadata(-1)); /// /// Holds the latest queued realization operation. /// private DispatcherOperation RealizeOperation { get; set; } /// /// Returns the that this panel hosts items for. /// /// public ItemsControl ItemsOwner { get { return ItemsControl.GetItemsOwner(this); } } /// /// Returns the index to an item that corresponds to the specified, generated . /// /// The that corresponds to the item index to be returned. /// An index to an item that corresponds to the specified if it was generated and hosted by this panel; otherwise, -1. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] public int IndexFromContainer(UIElement container) { if (container == null) { throw new ArgumentNullException("container"); } // The generator is technically not necessary, but it ensures the container was generated and hosted by this panel. var generator = ItemContainerGenerator as ItemContainerGenerator; if (generator != null && generator.ItemFromContainer(container) != DependencyProperty.UnsetValue) { var index = container.ReadLocalValue(IndexForItemContainerProperty); return index as int? ?? -1; } return -1; } /// /// Returns the item that corresponds to the specified, generated . /// /// The that corresponds to the item to be returned. /// An that is the item which corresponds to the specified if it was generated and hosted by this panel; otherwise, null. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] public object ItemFromContainer(UIElement container) { if (container == null) { throw new ArgumentNullException("container"); } var generator = ItemContainerGenerator as ItemContainerGenerator; if (generator != null) { var item = generator.ItemFromContainer(container); return item != DependencyProperty.UnsetValue ? item : null; } return null; } /// /// Returns the corresponding to the item at the given index within the item collection if it has been realized. /// /// The index of the desired item. /// The element corresponding to the item at the given index within the item collection or returns null if the item is not realized. public UIElement ContainerFromIndex(int itemIndex) { var generator = ItemContainerGenerator as ItemContainerGenerator; if (generator != null) { return generator.ContainerFromIndex(itemIndex) as UIElement; } return null; } /// /// Returns the corresponding to the given item if it has been realized. /// /// The item to find the for. /// A that corresponds to the given item. Returns null if the item does not belong to the item collection, or if a has not been generated for it. /// Use caution when calling this method as it does a linear search for the item. Consider calling instead. public UIElement ContainerFromItem(object item) { var generator = ItemContainerGenerator as ItemContainerGenerator; if (generator != null) { return generator.ContainerFromItem(item) as UIElement; } return null; } /// /// Invalidates the realization state of all items being hosted by this panel. After the invalidation, the panel will have its reality updated, which will occur asynchronously unless subsequently forced by . /// public void InvalidateReality() { if (RealizeOperation != null) { RealizeOperation.Abort(); } object state = null; Action action = null; action = delegate { RealizeOperation = null; state = RealizeCore(state); if (state != null && RealizeOperation == null) { RealizeOperation = Dispatcher.BeginInvoke(action, RealizationPriority); } }; RealizeOperation = Dispatcher.BeginInvoke(action, RealizationPriority); } /// /// Ensures that all items being hosted by this panel are properly realized or virtualized. /// public void UpdateReality() { RealizeOperation.Abort(); RealizeOperation = null; object state = null; do { state = RealizeCore(state); } while (state != null); } /// /// Manages calls to . /// /// A custom state object left over from a previous call to if additional processing was needed. /// A custom state object if additional processing is needed; otherwise, null. private object RealizeCore(object state) { if (IsItemsHost) { if (IsVirtualizing) { var owner = ItemsOwner; if (owner != null) { return RealizeOverride(owner.ItemsSource ?? owner.Items, state); } } else if (InternalChildren.Count == 0) { var generator = ItemContainerGenerator; using (generator.StartAt(new GeneratorPosition(-1, 0), GeneratorDirection.Forward)) { int index = 0; DependencyObject next; while ((next = generator.GenerateNext()) != null) { var container = next as UIElement; if (container != null) { container.SetValue(IndexForItemContainerProperty, index); AddInternalChild(container); generator.PrepareItemContainer(next); } index++; } } } } return null; } /// /// When overridden in a derived class, realizes and/or virtualizes items, optionally deferring additional realization. /// /// The current items being hosted by this panel. /// A custom state object left over from a previous call to if additional processing was needed. /// Implementations may optionally defer additional processing by return a non-null object, which will then be passed to a future call to . protected abstract object RealizeOverride(IEnumerable items, object state); /// /// Indicates that the property value has changed. /// /// The old property value. /// The new property value. protected override void OnIsItemsHostChanged(bool oldIsItemsHost, bool newIsItemsHost) { base.OnIsItemsHostChanged(oldIsItemsHost, newIsItemsHost); InvalidateReality(); } /// /// Indicates that the property value has changed. /// /// The old property value. /// The new property value. protected virtual void OnIsVirtualizingChanged(bool oldIsVirtualizing, bool newIsVirtualizing) { InvalidateReality(); } /// /// Maintains event handlers when the items source has changed. /// /// The that raised the event. /// Provides data for the event. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] protected override sealed void OnItemsChanged(object sender, ItemsChangedEventArgs args) { if (args == null) { throw new ArgumentNullException("args"); } var owner = ItemsOwner; var items = owner != null ? owner.Items : null; var action = args.Action; if (action == NotifyCollectionChangedAction.Reset) { OnItemsChanged(items, new NotifyCollectionChangedEventArgs(action)); } else if (items != null) { var generator = sender as IItemContainerGenerator; if (generator != null) { if (action == NotifyCollectionChangedAction.Add) { var index = generator.IndexFromGeneratorPosition(args.Position); // ItemContainerGenerator is infested with bugs. One of which is that as items are added to the end of a collection, the index from IndexFromGeneratorPosition will usually be wrong. if (args.Position.Offset == 1) { index = items.Count - 1; } var newItems = new VirtualItemsList(items, index, args.ItemCount); if (!IsVirtualizing) { for (int i = index; i < index + args.ItemCount; i++) { RealizeItem(i); } } OnItemsChanged(items, new NotifyCollectionChangedEventArgs(action, newItems, index)); } else if (action == NotifyCollectionChangedAction.Remove) { var oldIndex = generator.IndexFromGeneratorPosition(args.Position); var oldItems = new ArrayList(args.ItemCount); // Since we can't actually get the old items directly, and sometimes we can't even get the old index from the generator, we'll get as many as we can from the visuals. for (int i = 0; i < args.ItemUICount; i++) { var element = InternalChildren[args.Position.Index + i]; oldItems.Add(ItemFromContainer(element)); if (oldIndex == -1) { oldIndex = (int)element.ReadLocalValue(IndexForItemContainerProperty); } element.ClearValue(IndexForItemContainerProperty); } // Unfortunately ItemContainerGenerator always expects us to remove the child, even if we didn't want to. if (args.ItemUICount > 0) { RemoveInternalChildRange(args.Position.Index, args.ItemUICount); } OnItemsChanged(items, new NotifyCollectionChangedEventArgs(action, oldItems, oldIndex)); } else if (action == NotifyCollectionChangedAction.Move) { var oldIndex = -1; var index = generator.IndexFromGeneratorPosition(args.Position); var movedItems = new VirtualItemsList(items, index, args.ItemCount); var count = args.ItemUICount; if (count > 0) { var elements = new UIElement[count]; for (var i = 0; i < count; i++) { elements[i] = InternalChildren[args.OldPosition.Index + i]; if (oldIndex == -1) { oldIndex = (int)elements[i].ReadLocalValue(IndexForItemContainerProperty); } } RemoveInternalChildRange(args.OldPosition.Index + Math.Min(args.OldPosition.Offset, 1), count); for (var i = 0; i < count; i++) { InsertInternalChild(args.Position.Index + i, elements[i]); } } OnItemsChanged(items, new NotifyCollectionChangedEventArgs(action, movedItems, index, oldIndex)); } else if (action == NotifyCollectionChangedAction.Replace) { var index = generator.IndexFromGeneratorPosition(args.Position); var newItems = new VirtualItemsList(items, index, args.ItemCount); // Todo: Actually get the old items. ItemContainerGenerator doesn't make this easy. var oldItems = new VirtualItemsList(null, index, args.ItemCount); OnItemsChanged(items, new NotifyCollectionChangedEventArgs(action, newItems, oldItems, index)); } } } base.OnItemsChanged(sender, args); InvalidateReality(); } /// /// Called when the collection that is associated with the for this changes. /// /// The that raised the event. /// Provides data for the event. [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] protected virtual void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs args) { } /// /// Realizes an item container for the item with the given index. /// /// The index of the item. /// The child that was created and added to the internal children. protected UIElement RealizeItem(int itemIndex) { var generator = ItemContainerGenerator; var position = generator.GeneratorPositionFromIndex(itemIndex); using (generator.StartAt(position, GeneratorDirection.Forward, true)) { var isNewlyRealized = false; var container = generator.GenerateNext(out isNewlyRealized) as UIElement; if (position.Offset != 0 && container != null && isNewlyRealized) { container.SetValue(IndexForItemContainerProperty, itemIndex); InsertInternalChild(position.Index + 1, container); generator.PrepareItemContainer(container); } return container; } } /// /// Removes an item container for the item with the given index. /// /// The index of the item. /// true if the child had been previously realized and was now removed; otherwise, false. protected void VirtualizeItem(int itemIndex) { var generator = ItemContainerGenerator; if (generator != null) { var position = generator.GeneratorPositionFromIndex(itemIndex); if (position.Offset == 0) { generator.Remove(position, 1); InternalChildren[position.Index].ClearValue(IndexForItemContainerProperty); RemoveInternalChildRange(position.Index, 1); } } } /// /// Returns a list which represents a subset of the elements in the source list. /// /// /// This list is used in NotifyCollectionChangedEventArgs because we might be dealing with /// virtualized lists that raise events for items changing when the items haven't been /// loaded into memory yet. If the client needs to inspect the item, then they can index /// into this list and it will retrieve it from the original source, but if they don't need /// to inspect the item then we spare the cost of the lookup and retrieval. /// private class VirtualItemsList : IList { public VirtualItemsList(IList items, int offset, int count) { Items = items; Offset = offset; Count = count; } public IList Items { get; set; } public int Offset { get; set; } public int Count { get; set; } public object this[int index] { get { if (index < 0 || index >= Count) { throw new ArgumentOutOfRangeException("index"); } if (Items != null && index + Offset < Items.Count) { return Items[index + Offset]; } return null; } set { throw new NotSupportedException(); } } public int IndexOf(object value) { if (Items != null) { for (int i = 0; i < Count && i + Offset < Items.Count; i++) { if (Object.Equals(Items[i + Offset], value)) { return i; } } } return -1; } public bool Contains(object value) { return IndexOf(value) >= 0; } public IEnumerator GetEnumerator() { if (Items != null) { for (int i = 0; i < Count && i + Offset < Items.Count; i++) { yield return Items[i + Offset]; } } } #region Unsupported IList Members public int Add(object value) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public void Insert(int index, object value) { throw new NotSupportedException(); } public void Remove(object value) { throw new NotSupportedException(); } public void RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { throw new NotSupportedException(); } public bool IsFixedSize { get { return true; } } public bool IsReadOnly { get { return true; } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return null; } } #endregion } } } ================================================ FILE: WpfToolkit/Layout/ZoomableCanvas/ZoomableCanvas.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Controls.Extensions; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; namespace System.Windows.Controls { /// /// Defines an area within which you can explicitly position an infinite number of child elements by using coordinates that are relative to the area. /// public class ZoomableCanvas : VirtualPanel, IScrollInfo { #region ApplyTransformProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty ApplyTransformProperty = DependencyProperty.Register("ApplyTransform", typeof(bool), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsArrange, OnApplyTransformChanged)); /// /// Returns a transform applying the and when is set to true. /// /// Dependency object whos value is being coerced. /// The original uncoerced value. /// A new transform if is set to true; otherwise, . private static object CoerceRenderTransform(DependencyObject d, object value) { var canvas = d as ZoomableCanvas; if (canvas != null && canvas.ApplyTransform) { var transform = new TransformGroup(); transform.Children.Add(new ScaleTransform()); transform.Children.Add(new TranslateTransform()); return transform; } return value; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnApplyTransformChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(RenderTransformProperty); } /// /// Gets or sets whether to automatically apply a to the canvas. /// /// true or false. The default is . /// /// The value of this dependency property is true by default, meaning that the property will contain a that scales the canvas and its children automatically. /// This property can be set to false prevent the automatic transform. This means that children are responsible for changing their appearance when the property changes. /// Note that this property does not affect the placement of the elements; the children are automatically placed with the top-left corners of their elements at the appropriate positions on the screen, regardless of the value of . /// /// Children will usually do this by simply changing their and to become larger or smaller when the property increases or decreases. /// This is useful when pen widths are important, such as an element surrounded with a with set to 1.0. /// If is true, then as decreases the shape will be scaled down and the border stroke will become thinner than one pixel, possibly too thin to see even with sub-pixel rendering. /// This is also true when drawing paths, edges of a graph, or any other element that uses to draw lines and strokes. /// In these cases setting to false and setting the 's to while binding its and to a factor of will often provide a better effect. /// /// /// Another reason to set this property to false is when elements change their representation or visual state based on the scale (also known as "semantic zoom"). /// For example, imagine a canvas showing multiple thumbnails of spreadsheets and the relationships between their formulas and values. /// When is set to 1.0 (the default value), each spreadsheet element might be fully interactive, editable, and showing all rows and columns. /// When zooming out, and gets small enough that there is not enough room for each spreadsheet to show all of its rows and columns, it may change its representation into a bar chart or pie chart with axis values and a legend instead. /// When zooming even further out, and gets small enough that there is not enough room for the axis and legend, it may simply remove the axis and legend to make more room for the graphical portion of the chart. /// Since the children of the canvas can be arbitary rich UIElements, they can dynamically change their representation and be interacted with at all levels of zoom. /// This is in sharp contrast to multi-scale-image approaches such as Silverlight's Deep Zoom since those scenarios are simply performing linear scale transformations on pre-computed static bitmaps. /// /// public bool ApplyTransform { get { return (bool)GetValue(ApplyTransformProperty); } set { SetValue(ApplyTransformProperty, value); } } #endregion #region ActualViewboxProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty ActualViewboxProperty = DependencyProperty.RegisterReadOnly("ActualViewbox", typeof(Rect), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(Rect.Empty, OnActualViewboxChanged, CoerceActualViewbox)).DependencyProperty; /// /// Returns a representing the area of the canvas that is currently being displayed. /// /// Dependency object whos value is being coerced. /// The original uncoerced value. /// A representing the area of the canvas (in canvas coordinates) that is being displayed by this panel. private static object CoerceActualViewbox(DependencyObject d, object value) { var canvas = d as ZoomableCanvas; if (canvas != null) { var offset = canvas.Offset; var scale = canvas.Scale; var renderSize = canvas.RenderSize; value = new Rect(offset.X / scale, offset.Y / scale, renderSize.Width / scale, renderSize.Height / scale); } return value; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnActualViewboxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var canvas = d as ZoomableCanvas; if (canvas != null) { canvas.InvalidateReality(); } var scrollInfo = d as IScrollInfo; if (scrollInfo != null && scrollInfo.ScrollOwner != null) { scrollInfo.ScrollOwner.InvalidateScrollInfo(); } } /// /// Gets a representing the area of the canvas that is currently being displayed by this panel. /// /// A representing the area of the canvas that is currently being displayed by this panel. /// /// The value of this property is automatically computed based on the , , and of this panel. /// It is independent (and usually different) from the dependency property. /// /// public Rect ActualViewbox { get { return (Rect)GetValue(ActualViewboxProperty); } } #endregion #region ViewboxProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty ViewboxProperty = DependencyProperty.Register("Viewbox", typeof(Rect), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(Rect.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnViewboxChanged), IsViewboxValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsViewboxValid(object value) { var viewbox = (Rect)value; return viewbox.IsEmpty || (viewbox.X.IsBetween(Double.MinValue, Double.MaxValue) && viewbox.Y.IsBetween(Double.MinValue, Double.MaxValue) && viewbox.Width.IsBetween(Double.Epsilon, Double.MaxValue) && viewbox.Height.IsBetween(Double.Epsilon, Double.MaxValue)); } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnViewboxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(ScaleProperty); d.CoerceValue(OffsetProperty); } /// /// Gets or sets the portion of the canvas (in canvas coordinates) that should be attempted to be displayed by this panel. /// /// A specifying the portion of the canvas that should be displayed by this panel, or when unspecified. The default value is . /// /// The area of the canvas shown by this panel can be controlled by either setting and , or by setting the , , and properties. /// When is set to anything other than , the and will be automatically coerced to appropriate values according to the and properties. /// Note that the mode of is not supported, so unless the aspect ratio of exactly matches the aspect ratio of the actual area displayed will be more or less than . /// The exact area that is displayed can be determined by the property in this case. /// /// /// /// public Rect Viewbox { get { return (Rect)GetValue(ViewboxProperty); } set { SetValue(ViewboxProperty, value); } } #endregion #region StretchProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(Stretch.Uniform, OnStretchChanged), IsStretchValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsStretchValid(object value) { var stretch = (Stretch)value; return stretch == Stretch.None || stretch == Stretch.Uniform || stretch == Stretch.UniformToFill; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnStretchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(ScaleProperty); d.CoerceValue(OffsetProperty); } /// /// Gets or sets a value that specifies how the content of the canvas is displayed when is set. /// /// One of the values other than . The default is . /// /// Please see the documentation of for a detailed explanation of the effects of this property. /// The mode of is not supported, so unless the aspect ratio of exactly matches the aspect ratio of the actual area displayed will be more or less than . /// The exact area that is displayed can be determined by the property in this case. /// /// public Stretch Stretch { get { return (Stretch)GetValue(StretchProperty); } set { SetValue(StretchProperty, value); } } #endregion #region StretchDirectionProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty StretchDirectionProperty = DependencyProperty.Register("StretchDirection", typeof(StretchDirection), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(StretchDirection.Both, OnStretchDirectionChanged), IsStretchDirectionValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsStretchDirectionValid(object value) { var stretch = (StretchDirection)value; return stretch == StretchDirection.Both || stretch == StretchDirection.UpOnly || stretch == StretchDirection.DownOnly; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnStretchDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(ScaleProperty); d.CoerceValue(OffsetProperty); } /// /// Gets or sets how setting the property can affect the . /// /// One of the values. The default is /// /// When setting the property, the and properties are automatically coerced to the appropriate values according to the and properties, and any existing values of and will be overridden. /// However, when the value of is set to anything other than , then the setting of the property can limit the range of the automatically computed value. /// The exact area that is displayed can be determined by the property in this case. /// public StretchDirection StretchDirection { get { return (StretchDirection)GetValue(StretchDirectionProperty); } set { SetValue(StretchDirectionProperty, value); } } #endregion #region OffsetProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register("Offset", typeof(Point), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(new Point(0.0, 0.0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnOffsetChanged, CoerceOffset), IsOffsetValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsOffsetValid(object value) { var point = (Point)value; return point.X.IsBetween(Double.MinValue, Double.MaxValue) && point.Y.IsBetween(Double.MinValue, Double.MaxValue); } /// /// Returns a representing top-left point of the canvas (in canvas coordinates) that is currently being displayed after taking into account. /// /// Dependency object whos value is being coerced. /// The original uncoerced value. /// A representing top-left point of the canvas (in canvas coordinates) that is currently being displayed. private static object CoerceOffset(DependencyObject d, object value) { var canvas = d as ZoomableCanvas; if (canvas != null) { var viewbox = canvas.Viewbox; if (!viewbox.IsEmpty) { var scale = canvas.Scale; var renderSize = canvas.RenderSize; value = new Point((viewbox.X + viewbox.Width / 2) * scale - renderSize.Width / 2, (viewbox.Y + viewbox.Height / 2) * scale - renderSize.Height / 2); } } return value; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(ActualViewboxProperty); var canvas = d as ZoomableCanvas; if (canvas != null) { canvas.OffsetOverride((Point)e.NewValue); } } /// /// Gets or sets (but see remarks) the top-left point of the area of the canvas (in canvas coordinates) that is being displayed by this panel. /// /// A on the canvas (in canvas coordinates). The default is (0,0). /// /// This value controls the horizontal and vertical position of the canvas children relative to this panel. /// /// For example, consider a child element which has its and set to 100 and 100. /// Also assume that the value of is set to 1.0 (the default value). /// /// /// By default, the value of is (0,0) so the element will be displayed at 100 units to the right and 100 units down from the top-left corner of this panel, exactly how would display it. /// If the value of is set to (20,40) then the element will be displayed 80 units to the right and 60 units down from the top-left corner of this panel. /// In other words, it will have appeared to "move" up by 20 units and left by 40 units. /// /// /// If the value of is set to (100,100) then the top-left corner of the element will be displayed exactly in the top-left corner of this panel. /// Note that this is true regardless of the value of ! /// /// /// If the value of is set to (110,120) then the top-left corner of the element will be displayed 10 units to the left and 20 units above this panel. /// In other words, if is set to true, then the top-left corner of the element will not be visible. /// /// /// The value of can also be negative, so if the value of is set to (-100,-100) then the element will be displayed at 200 pixels to the right and 200 pixels down from the top-left corner of the panel. /// /// /// When the property is set to a non- value, the value of the property will be automatically computed to match the , , and properties. /// The value of the property will contain the computed value (via the WPF dependency property coersion mechanism), and any attempts to set to a different value will be ignored until is set to again. /// /// /// public Point Offset { get { return (Point)GetValue(OffsetProperty); } set { SetValue(OffsetProperty, value); } } #endregion #region ScaleProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(double), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnScaleChanged, CoerceScale), IsScaleValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsScaleValid(object value) { return ((double)value).IsBetween(Double.Epsilon, Double.MaxValue); } /// /// Returns a representing the scale of the content that is currently being displayed after taking , , and into account. /// /// Dependency object whos value is being coerced. /// The original uncoerced value. /// A representing scale of the content that is currently being displayed. private static object CoerceScale(DependencyObject d, object value) { var scale = (double)value; var canvas = d as ZoomableCanvas; if (canvas != null) { var renderSize = canvas.RenderSize; if (renderSize.Width > 0 && renderSize.Height > 0) { var viewbox = canvas.Viewbox; if (!viewbox.IsEmpty) { switch (canvas.Stretch) { case Stretch.Uniform: scale = Math.Min(renderSize.Width / viewbox.Width, renderSize.Height / viewbox.Height); break; case Stretch.UniformToFill: scale = Math.Max(renderSize.Width / viewbox.Width, renderSize.Height / viewbox.Height); break; } switch (canvas.StretchDirection) { case StretchDirection.DownOnly: scale = scale.AtMost((double)value); break; case StretchDirection.UpOnly: scale = scale.AtLeast((double)value); break; } } } } return scale; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(ActualViewboxProperty); d.CoerceValue(OffsetProperty); var canvas = d as ZoomableCanvas; if (canvas != null) { canvas.ScaleOverride((double)e.NewValue); } } /// /// Gets or sets (but see remarks) the scale at which the content of the canvas is being displayed. /// /// A between and . The default value is 1.0. /// /// This value is what controls the zoom level of the canvas and the amount of the when is set to true. /// When is set to false, this value still controls the positioning of the children (i.e. elements are placed closer together when zoomed out and farther apart when zoomed in), but the sizes of the children are unaffected. /// /// For example, consider a child element which has its and set to 100 and 100, with a of 50 and a of 50. /// Also assume that the value of is set to (0,0) (the default value). /// /// /// By default, the value of is 1.0 so the element will be displayed at 100 units to the right and 100 units down from the top-left corner of this panel, exactly how would display it. /// If the value of is set to 0.8 then the top-left corner of the element will be displayed 80 units to the right and 80 units down from the top-left corner of this panel. /// If is set to true (the default value), then the element will also be scaled down (shrunk) to 80% of its normal size, so that the bottom-right of the element will be 120 units to the right and 120 units down from the top-left corner of this panel. /// If is set to false, then the element will remain its original size, resulting in the bottom-right of the element being 130 units to the right and 130 units down from the top-left corner of this panel. /// In other words, it will simply have appeared to "move" up by 20 units and left by 20 units without changing its size. /// This is not normally what a user would expect when "zooming out" (unless the element is some kind of floating label above the canvas), so it is expected that the children of the canvas will be responsible for changing their representation appropriately when is set to false. /// /// /// When the property is set to a non- value, the value of the property will be automatically computed to match the , , and properties. /// The value of the property will contain the computed value (via the WPF dependency property coersion mechanism), but and any attempts to set to a different value will be ignored (if is set to ) until is set to again. /// /// /// public double Scale { get { return (double)GetValue(ScaleProperty); } set { SetValue(ScaleProperty, value); } } #endregion #region RealizationLimitProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty RealizationLimitProperty = DependencyProperty.Register("RealizationLimit", typeof(int), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(int.MaxValue, OnRealizationLimitChanged), IsRealizationLimitValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsRealizationLimitValid(object value) { return (int)value >= 0; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnRealizationLimitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var canvas = d as ZoomableCanvas; if (canvas != null) { canvas.InvalidateReality(); } } /// /// Gets or sets the maximum number of elements that will be instantiated on the canvas when is set to . /// /// An between 0 and . The default is . /// /// When the children of the canvas are being populated through an , visual elements will be instantiated for the first items within the . /// public int RealizationLimit { get { return (int)GetValue(RealizationLimitProperty); } set { SetValue(RealizationLimitProperty, value); } } #endregion #region RealizationRateProperty /// /// Identifies the dependency property. /// public static readonly DependencyProperty RealizationRateProperty = DependencyProperty.Register("RealizationRate", typeof(int), typeof(ZoomableCanvas), new FrameworkPropertyMetadata(int.MaxValue, OnRealizationRateChanged), IsRealizationRateValid); /// /// Determines whether the value given is a valid value for the dependency property. /// /// The potential value for the dependency property. /// true if the value is a valid value for the property; otherwise, false. private static bool IsRealizationRateValid(object value) { return (int)value >= 0; } /// /// Handles the event that occurs when the value of the dependency property has changed. /// /// The dependency object on which the dependency property has changed. /// The event args containing the old and new values of the dependency property. private static void OnRealizationRateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var canvas = d as ZoomableCanvas; if (canvas != null) { canvas.InvalidateReality(); } } /// /// Gets or sets the maximum number of elements that will be realized or virtualized at one time before yielding control back to the dispatcher when is set to . /// /// An between 0 and . The default is . /// /// By default, the value of this property is which means that all realization and virtualization happens at once, at the time determined by . /// The default behavior is optimized to realize all elements as quickly as possible, at the expensive of application responsiveness while the realization is happening. /// Setting the to and decreasing the will make the application feel more responsive but will take longer to realize all items. /// public int RealizationRate { get { return (int)GetValue(RealizationRateProperty); } set { SetValue(RealizationRateProperty, value); } } #endregion #region Initialization /// /// Performs a one-time initialization of -related metadata. /// [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] static ZoomableCanvas() { RenderTransformProperty.OverrideMetadata(typeof(ZoomableCanvas), new FrameworkPropertyMetadata(null, CoerceRenderTransform)); try { Canvas.TopProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(OnPositioningChanged)); Canvas.LeftProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(OnPositioningChanged)); Canvas.BottomProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(OnPositioningChanged)); Canvas.RightProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(OnPositioningChanged)); } catch (ArgumentException) { // These overrides crash in Expression Blend. } } /// /// Ensures coersion routines are invoked with their default values. /// public ZoomableCanvas() { CoerceValue(ScaleProperty); CoerceValue(OffsetProperty); CoerceValue(ActualViewboxProperty); CoerceValue(RenderTransformProperty); } #endregion #region Spatial Item Management /// /// Provides a two-dimensional index of items that can be quickly queried for all items that intersect a given rectangle. /// /// /// When the is hosting items for an , the can implement this interface to greatly speed up virtualization in the canvas. /// If any of those conditions are not true, then the canvas must realize every item at least once in order to determine its bounds before it can virtualize it, and then once it is virtualized it will have no means of moving spontaneously back into view. /// [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "This interface is only used by ZoomableCanvas.")] public interface ISpatialItemsSource { /// /// Gets the entire extent of the index, which is typically the union of all bounding boxes of all items within the set. /// /// /// This value is used when determining the extent of the scroll bars when the canvas is hosted in a scroll viewer. /// Rect Extent { get; } /// /// Gets the set of items that intersect the given rectangle. /// /// The area in which any intersecting items are returned. /// A result set of all items that intersect the given rectangle. /// /// The enumerator returned by this method is used lazily and sometimes only partially, meaning it should return quickly without computing the entire result set immediately for best results. /// IEnumerable Query(Rect rectangle); /// /// Occurs when the value of the property has changed. /// event EventHandler ExtentChanged; /// /// Occurs when the results of the last query are no longer valid and should be re-queried. /// event EventHandler QueryInvalidated; } #region Private SpatialIndex Implementation /// /// Private implementation of when the items source is not one. /// /// /// This class efficiently implements a spatial index by internally using a PriorityQuadTree data structure. /// private class PrivateSpatialIndex : ISpatialItemsSource { /// /// Private class that holds an index/bounds pair. /// /// /// A Tuple could have been used instead except that we want Index to be mutable. /// private class SpatialItem { public SpatialItem() { Index = -1; Bounds = Rect.Empty; } public int Index { get; set; } public Rect Bounds { get; set; } public override string ToString() { return "Item[" + Index + "].Bounds = " + Bounds; } } /// /// We use a PriorityQuadTree to implement our spatial index. /// private readonly PriorityQuadTree _tree = new PriorityQuadTree(); /// /// This is a list of all of the spatial items in the index. /// private readonly List _items = new List(); /// /// Holds the accurate extent of all item bounds in the index. This may be different from _tree.Extent. /// private Rect _extent = Rect.Empty; /// /// Holds the last query used in order to know when to raise the event. /// private Rect _lastQuery = Rect.Empty; /// /// Occurs when the value of the property has changed. /// public event EventHandler ExtentChanged; /// /// Occurs when the results of the last query are no longer valid and should be re-queried. /// public event EventHandler QueryInvalidated; /// /// Get a list of the items that intersect the given bounds. /// /// The bounds to test. /// /// List of zero or more items that intersect the given bounds, returned in the order given by the priority assigned during Insert. /// public IEnumerable Query(Rect bounds) { _lastQuery = bounds; return _tree.GetItemsIntersecting(bounds).Select(i => i.Index); } /// /// Gets the computed minimum required rectangle to contain all of the items in the index. This property is also settable for efficiency the future extent of the items is known. /// public Rect Extent { get { if (_extent.IsEmpty) { foreach (var item in _items) { _extent.Union(item.Bounds); } } return _extent; } } /// /// Gets or sets the bounds for the item with the given . /// /// The index of the item. /// The bounds of the item, or if the bounds are unknown. /// /// Items with bounnds of are always returned first from any query. /// public Rect this[int index] { get { return _items[index].Bounds; } set { var item = _items[index]; var bounds = item.Bounds; if (bounds != value) { _extent = Rect.Empty; _tree.Remove(item, bounds); _tree.Insert(item, value, value.IsEmpty ? Double.PositiveInfinity : value.Width + value.Height); item.Bounds = value; if (ExtentChanged != null) { ExtentChanged(this, EventArgs.Empty); } if (QueryInvalidated != null && (bounds.IntersectsWith(_lastQuery) || value.IntersectsWith(_lastQuery))) { QueryInvalidated(this, EventArgs.Empty); } } } } /// /// Adds or inserts the given of items at the given . /// /// The index at which to insert the items. /// The number of items to insert. /// /// All items are inserted with bounds of , meaning they will be returned from all queries. /// public void InsertRange(int index, int count) { var items = new SpatialItem[count]; for (int i = 0; i < count; i++) { items[i] = new SpatialItem(); items[i].Index = index + i; _tree.Insert(items[i], Rect.Empty, Double.PositiveInfinity); } _items.InsertRange(index, items); if (QueryInvalidated != null) { QueryInvalidated(this, EventArgs.Empty); } } /// /// Removes the given of items at the given . /// /// The index at which to remove from. /// The number of items to remove. public void RemoveRange(int index, int count) { for (int i = index; i < _items.Count; i++) { if (i < index + count) { _tree.Remove(_items[i], _items[i].Bounds); } else { _items[i].Index = i - count; } } _items.RemoveRange(index, count); _extent = Rect.Empty; if (ExtentChanged != null) { ExtentChanged(this, EventArgs.Empty); } if (QueryInvalidated != null) { QueryInvalidated(this, EventArgs.Empty); } } /// /// Clears and resets the spatial index to hold the given of items. /// /// The number of items within the index. public void Reset(int count) { _extent = Rect.Empty; _items.Clear(); InsertRange(0, count); if (ExtentChanged != null) { ExtentChanged(this, EventArgs.Empty); } if (QueryInvalidated != null) { QueryInvalidated(this, EventArgs.Empty); } } /// /// Optimizes the spatial index based on the current extent if optimization is warranted. /// public void Optimize() { var treeExtent = _tree.Extent; var realExtent = Extent; if (treeExtent.Top - realExtent.Top > treeExtent.Height || treeExtent.Left - realExtent.Left > treeExtent.Width || realExtent.Right - treeExtent.Right > treeExtent.Width || realExtent.Bottom - treeExtent.Bottom > treeExtent.Height) { _tree.Extent = realExtent; if (QueryInvalidated != null) { QueryInvalidated(this, EventArgs.Empty); } } } } #endregion /// /// Two-dimentional spatial index of our data items. /// private ISpatialItemsSource SpatialIndex; /// /// Private implementation of when the items source does not provide one. /// private PrivateSpatialIndex PrivateIndex; /// /// Ordered list of realized items based on the order they are returned from the spatial index. /// private LinkedList RealizedItems; /// /// Handles the event that occurs when the value of the property has changed. /// protected override void OnIsVirtualizingChanged(bool oldIsVirtualizing, bool newIsVirtualizing) { OnItemsReset(); base.OnIsVirtualizingChanged(oldIsVirtualizing, newIsVirtualizing); } /// /// Refreshes our data when the property has changed. /// protected override void OnIsItemsHostChanged(bool oldIsItemsHost, bool newIsItemsHost) { OnItemsReset(); base.OnIsItemsHostChanged(oldIsItemsHost, newIsItemsHost); } /// /// Dispatches to specific methods to update the spatial index when the items have changed. /// protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs args) { if (args == null) { throw new ArgumentNullException("args"); } if (args.Action == NotifyCollectionChangedAction.Add) { OnItemsAdded(args.NewStartingIndex, args.NewItems); } else if (args.Action == NotifyCollectionChangedAction.Remove) { OnItemsRemoved(args.OldStartingIndex, args.OldItems); } else if (args.Action == NotifyCollectionChangedAction.Reset) { OnItemsReset(); } InvalidateReality(); InvalidateExtent(); } /// /// Updates our private spatial index when items are added to the item source. /// /// The index of the first item that was added. /// The items that were added. private void OnItemsAdded(int index, IList items) { if (PrivateIndex != null) { PrivateIndex.InsertRange(index, items.Count); } if (RealizedItems != null) { var item = RealizedItems.First; while (item != null) { if (item.Value >= index) { item.Value += items.Count; } item = item.Next; } } } /// /// Updates our private spatial index when items are removed from the item source. /// /// The old index of the first item that was removed. /// The items that were removed. private void OnItemsRemoved(int index, IList items) { if (PrivateIndex != null) { PrivateIndex.RemoveRange(index, items.Count); } if (RealizedItems != null) { var item = RealizedItems.First; while (item != null) { var next = item.Next; if (item.Value >= index) { if (item.Value < index + items.Count) { RealizedItems.Remove(item); } else { item.Value -= items.Count; } } item = next; } } } /// /// Resets and initializes our spatial indices when the items source has changed. /// private void OnItemsReset() { if (SpatialIndex != null) { SpatialIndex.ExtentChanged -= OnSpatialExtentChanged; SpatialIndex.QueryInvalidated -= OnSpatialQueryInvalidated; } RealizedItems = null; SpatialIndex = null; PrivateIndex = null; if (IsVirtualizing && IsItemsHost && ItemsOwner != null) { RealizedItems = new LinkedList(); SpatialIndex = ItemsOwner.ItemsSource as ISpatialItemsSource; if (SpatialIndex == null) { PrivateIndex = new PrivateSpatialIndex(); PrivateIndex.Reset(ItemsOwner.Items != null ? ItemsOwner.Items.Count : 0); SpatialIndex = PrivateIndex; } SpatialIndex.ExtentChanged += OnSpatialExtentChanged; SpatialIndex.QueryInvalidated += OnSpatialQueryInvalidated; } InvalidateReality(); InvalidateExtent(); } /// /// Invalidates reality when the last spatial query is no longer valid. /// /// The spatial index. /// The event arguments. private void OnSpatialQueryInvalidated(object sender, EventArgs e) { InvalidateReality(); } /// /// Invalidates the extent when the spatial index extent has changed. /// /// The spatial index. /// The event arguments. private void OnSpatialExtentChanged(object sender, EventArgs e) { InvalidateExtent(); } #endregion #region Virtualization /// /// Performs realization and virtualization in batches based on the . /// /// The current items being hosted by this panel. /// The previous return value of this method. /// A non-null value if further realization is required; otherwise, null. protected override object RealizeOverride(IEnumerable items, object state) { var enumerator = state as IEnumerator ?? RealizeOverride(); var rate = RealizationRate; while (rate-- > 0) { if (!enumerator.MoveNext()) { return null; } } return enumerator; } /// /// Realizes and virtualizes items based on the current viewbox. /// /// An enumerator which allows this method to continue realization where it left off. private IEnumerator RealizeOverride() { if (SpatialIndex != null) { // Optimize our private spatial index for the upcoming query. if (PrivateIndex != null) { PrivateIndex.Optimize(); } IEnumerable query; if (IsVirtualizing) { // Only realize the items within our viewbox. var viewbox = ActualViewbox; var limit = RealizationLimit; // Buffer the viewbox so that panning by small amounts is smooth. viewbox.Inflate(viewbox.Width / 10, viewbox.Height / 10); // Query the index for all items that intersect our viewbox, up to our realization limit. query = SpatialIndex.Query(viewbox).Take(limit); } else { // Get all items. query = SpatialIndex.Query(new Rect(Double.NegativeInfinity, Double.NegativeInfinity, Double.PositiveInfinity, Double.PositiveInfinity)); } // We insert nodes at the head of the linked list in the order they are returned. LinkedListNode lastNode = null; LinkedListNode nextNode = RealizedItems.First; // Realize them. foreach (var index in query) { // See if the item was already realized. var node = RealizedItems.FindNext(lastNode, index); if (node == null || node != nextNode) { if (node != null) { // If it was already realized further down the list, remove it from the list so we can add it later. RealizedItems.Remove(node); } else { // If it was not already realized, realize it now and create a new node for it. RealizeItem(index); node = new LinkedListNode(index); } // Insert the node after the last node, or at the front if this is the first. if (lastNode == null) { RealizedItems.AddFirst(node); } else { RealizedItems.AddAfter(lastNode, node); } } // Keep track of the last node of the query results. lastNode = node; nextNode = node.Next; // Yield control for throttling. yield return index; } // Virtualize any remaining items that are no longer part of our result set, backwards. nextNode = RealizedItems.Last; while (nextNode != lastNode) { var node = nextNode; nextNode = nextNode.Previous; var index = node.Value; var container = ContainerFromIndex(index); if (container == null || (!container.IsMouseCaptureWithin && !container.IsKeyboardFocusWithin)) { VirtualizeItem(index); RealizedItems.Remove(node); } // Yield control for throttling. yield return index; } } } #endregion #region Arrange Logic /// /// Updates the calculated and the and when the size changes. /// /// Size information about the render size. protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { CoerceValue(ScaleProperty); CoerceValue(OffsetProperty); CoerceValue(ActualViewboxProperty); base.OnRenderSizeChanged(sizeInfo); } /// /// Invalidates the arrangement of canvases when their children's positions change. /// /// Dependency object whos position has changed. /// Event arguments related to the change. private static void OnPositioningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var parent = VisualTreeHelper.GetParent(d); var zoomable = parent as ZoomableCanvas; if (zoomable != null) { zoomable.InvalidateArrange(); } else { var regular = parent as Canvas; if (regular != null) { regular.InvalidateArrange(); } } } /// /// Gets the applied scale transform if is set to true. /// private ScaleTransform AppliedScaleTransform { get { if (ApplyTransform) { return (ScaleTransform)((TransformGroup)RenderTransform).Children[0]; } return null; } } /// /// Gets the applied translate transform if is set to true. /// private TranslateTransform AppliedTranslateTransform { get { if (ApplyTransform) { return (TranslateTransform)((TransformGroup)RenderTransform).Children[1]; } return null; } } /// /// Scales the child elements of a by applying a transform if is true, or by calling otherwise. /// /// The new scale of the canvas. protected virtual void ScaleOverride(double scale) { var appliedTransform = AppliedScaleTransform; if (appliedTransform != null) { appliedTransform.ScaleX = scale; appliedTransform.ScaleY = scale; } else { InvalidateArrange(); } } /// /// Offsets the child elements of a by applying a transform if is true, or by calling otherwise. /// /// The new offset of the canvas. protected virtual void OffsetOverride(Point offset) { var appliedTransform = AppliedTranslateTransform; if (appliedTransform != null) { appliedTransform.X = -offset.X; appliedTransform.Y = -offset.Y; } else { InvalidateArrange(); } } /// /// Measures the child elements of a in anticipation of arranging them during the pass. /// /// An upper limit that should not be exceeded. /// A that represents the size that is required to arrange child content. protected override Size MeasureOverride(Size availableSize) { Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity); foreach (UIElement child in InternalChildren) { if (child != null) { child.Measure(childConstraint); } } return new Size(); } /// /// Arranges the content of a element. /// /// The size that this element should use to arrange its child elements. /// A that represents the arranged size of this element and its descendants. protected override Size ArrangeOverride(Size finalSize) { bool applyTransform = ApplyTransform; Point offset = applyTransform ? new Point() : Offset; double scale = applyTransform ? 1.0 : Scale; ChildrenExtent = Rect.Empty; foreach (UIElement child in InternalChildren) { if (child != null) { // Get bounds information from the element. Rect bounds = new Rect(Canvas.GetLeft(child).GetValueOrDefault(), Canvas.GetTop(child).GetValueOrDefault(), child.DesiredSize.Width / scale, child.DesiredSize.Height / scale); // If we are maintaining our own spatial wrapper then update its bounds. if (PrivateIndex != null) { int index = IndexFromContainer(child); Rect oldBounds = PrivateIndex[index]; const double tolerance = .001; // The exact values during arrange can vary slightly. if (Math.Abs(oldBounds.Top - bounds.Top) > tolerance || Math.Abs(oldBounds.Left - bounds.Left) > tolerance || Math.Abs(oldBounds.Width - bounds.Width) > tolerance || Math.Abs(oldBounds.Height - bounds.Height) > tolerance) { PrivateIndex[index] = bounds; } } // Update the children extent for scrolling. ChildrenExtent.Union(bounds); // So far everything has been in canvas coordinates. Here we adjust the result for the final call to Arrange. bounds.X *= scale; bounds.X -= offset.X; bounds.Y *= scale; bounds.Y -= offset.Y; bounds.Width *= scale; bounds.Height *= scale; // WPF Arrange will crash if the values are too large. bounds.X = bounds.X.AtLeast(Single.MinValue / 2); bounds.Y = bounds.Y.AtLeast(Single.MinValue / 2); bounds.Width = bounds.Width.AtMost(Single.MaxValue); bounds.Height = bounds.Height.AtMost(Single.MaxValue); child.Arrange(bounds); } } InvalidateExtent(); return finalSize; } /// /// Returns a clipping geometry that indicates the area that will be clipped if the property is set to true. /// /// The available size of the element. /// A that represents the area that is clipped if is true. protected override Geometry GetLayoutClip(Size layoutSlotSize) { // ZoomableCanvas only clips to bounds if ClipToBounds is set, no automatic clipping. return ClipToBounds ? new RectangleGeometry(new Rect(RenderSize)) : null; } #endregion #region Utility Methods /// /// Represents the extent of the instantiated UIElements calculated during . /// private Rect ChildrenExtent = Rect.Empty; /// /// Caches the calculated based on the spatial index and arranged children of the canvas until is called. /// private Rect ComputedExtent = Rect.Empty; /// /// Gets the extent of the populated area of the canvas (in canvas coordinates). /// /// /// This property is also used to determine the range of the scroll bars when the canvas is hosted within a . /// public virtual Rect Extent { get { if (ComputedExtent.IsEmpty) { ComputedExtent = Rect.Union(ChildrenExtent, SpatialIndex != null ? SpatialIndex.Extent : Rect.Empty); } return ComputedExtent; } } /// /// Re-computes the of items in the canvas and updates the parent scroll viewer if there is one. /// protected void InvalidateExtent() { ComputedExtent = Rect.Empty; var owner = ((IScrollInfo)this).ScrollOwner; if (owner != null) { owner.InvalidateScrollInfo(); } } /// /// Gets the current visual coordinates for a given on this . /// /// The in canvas coordinates. /// The current position of the canvas point on the screen relative to the upper-left corner of this . public Point GetVisualPoint(Point canvasPoint) { return (Point)(((Vector)canvasPoint * Scale) - (Vector)Offset); } /// /// Gets the point on the canvas that is currently represented by the given on the screen. /// /// The on the screen relative to the upper-left corner of this . /// The point on the canvas that corresponds to the given point on the screen. public Point GetCanvasPoint(Point screenPoint) { return (Point)(((Vector)Offset + (Vector)screenPoint) / Scale); } /// /// Returns the the point on the canvas at which the mouse cursor is currently located. /// public Point MousePosition { get { var position = Mouse.GetPosition(this); if (ApplyTransform) { return position; } else { return GetCanvasPoint(position); } } } #endregion #region IScrollInfo Implementation [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] ScrollViewer IScrollInfo.ScrollOwner { get; set; } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] bool IScrollInfo.CanHorizontallyScroll { get; set; } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] bool IScrollInfo.CanVerticallyScroll { get; set; } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] double IScrollInfo.ViewportHeight { get { return ActualViewbox.Height * Scale; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] double IScrollInfo.ViewportWidth { get { return ActualViewbox.Width * Scale; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] double IScrollInfo.ExtentHeight { get { return Math.Max(Math.Max(this.ActualViewbox.Bottom, this.Extent.Bottom) - Math.Min(this.ActualViewbox.Top, this.Extent.Top), 0.0) * Scale; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] double IScrollInfo.ExtentWidth { get { return Math.Max(Math.Max(this.ActualViewbox.Right, this.Extent.Right) - Math.Min(this.ActualViewbox.Left, this.Extent.Left), 0.0) * Scale; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] double IScrollInfo.HorizontalOffset { get { return Math.Max(this.ActualViewbox.X - Extent.X, 0.0) * this.Scale; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] double IScrollInfo.VerticalOffset { get { return Math.Max(this.ActualViewbox.Y - Extent.Y, 0.0) * this.Scale; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.LineDown() { ((IScrollInfo)this).SetVerticalOffset(((IScrollInfo)this).VerticalOffset + 16); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.LineLeft() { ((IScrollInfo)this).SetHorizontalOffset(((IScrollInfo)this).HorizontalOffset - 16); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.LineRight() { ((IScrollInfo)this).SetHorizontalOffset(((IScrollInfo)this).HorizontalOffset + 16); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.LineUp() { ((IScrollInfo)this).SetVerticalOffset(((IScrollInfo)this).VerticalOffset - 16); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.MouseWheelDown() { ((IScrollInfo)this).SetVerticalOffset(((IScrollInfo)this).VerticalOffset + 48); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.MouseWheelLeft() { ((IScrollInfo)this).SetHorizontalOffset(((IScrollInfo)this).HorizontalOffset - 48); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.MouseWheelRight() { ((IScrollInfo)this).SetHorizontalOffset(((IScrollInfo)this).HorizontalOffset + 48); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.MouseWheelUp() { ((IScrollInfo)this).SetVerticalOffset(((IScrollInfo)this).VerticalOffset - 48); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.PageDown() { ((IScrollInfo)this).SetVerticalOffset(((IScrollInfo)this).VerticalOffset + ((IScrollInfo)this).ViewportHeight); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.PageLeft() { ((IScrollInfo)this).SetHorizontalOffset(((IScrollInfo)this).HorizontalOffset - ((IScrollInfo)this).ViewportWidth); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.PageRight() { ((IScrollInfo)this).SetHorizontalOffset(((IScrollInfo)this).HorizontalOffset + ((IScrollInfo)this).ViewportWidth); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.PageUp() { ((IScrollInfo)this).SetVerticalOffset(((IScrollInfo)this).VerticalOffset - ((IScrollInfo)this).ViewportHeight); } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.SetHorizontalOffset(double offset) { offset = Math.Max(Math.Min(offset, ((IScrollInfo)this).ExtentWidth - ((IScrollInfo)this).ViewportWidth), 0.0); var viewbox = Viewbox; if (viewbox.IsEmpty) { Offset = new Point(Offset.X + offset - ((IScrollInfo)this).HorizontalOffset, Offset.Y); } else { viewbox.X += (offset - ((IScrollInfo)this).HorizontalOffset) / Scale; Viewbox = viewbox; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] void IScrollInfo.SetVerticalOffset(double offset) { offset = Math.Max(Math.Min(offset, ((IScrollInfo)this).ExtentHeight - ((IScrollInfo)this).ViewportHeight), 0.0); var viewbox = Viewbox; if (viewbox.IsEmpty) { Offset = new Point(Offset.X, Offset.Y + offset - ((IScrollInfo)this).VerticalOffset); } else { viewbox.Y += (offset - ((IScrollInfo)this).VerticalOffset) / Scale; Viewbox = viewbox; } } [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] Rect IScrollInfo.MakeVisible(Visual visual, Rect rectangle) { if (rectangle.IsEmpty || visual == null || !IsAncestorOf(visual)) { return Rect.Empty; } rectangle = visual.TransformToAncestor(this).TransformBounds(rectangle); rectangle = RenderTransform.TransformBounds(rectangle); var width = ((IScrollInfo)this).ViewportWidth; var height = ((IScrollInfo)this).ViewportHeight; var left = -rectangle.X; var right = left + width - rectangle.Width; var top = -rectangle.Y; var bottom = top + height - rectangle.Height; var deltaX = left > 0 && right > 0 ? Math.Min(left, right) : left < 0 && right < 0 ? Math.Max(left, right) : 0.0; var deltaY = top > 0 && bottom > 0 ? Math.Min(top, bottom) : top < 0 && bottom < 0 ? Math.Max(top, bottom) : 0.0; var offset = Offset; offset.X -= deltaX; offset.Y -= deltaY; Offset = offset; rectangle.X += deltaX; rectangle.Y += deltaY; rectangle.Intersect(new Rect(0, 0, width, height)); return rectangle; } #endregion } } ================================================ FILE: WpfToolkit/Resources/ExceptionStringTable.txt ================================================ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; US-EN String Table ; Default Resource (used for English and non-represented locales) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; DataGrid Automation Strings DataGridCellItemAutomationPeer_NameCoreFormat=Item: {0}, Column Display Index: {1} CalendarAutomationPeer_CalendarButtonLocalizedControlType=Calendar button CalendarAutomationPeer_DayButtonLocalizedControlType=Day button CalendarAutomationPeer_BlackoutDayHelpText=Blackout Day - {0} Calendar_NextButtonName=Next button Calendar_PreviousButtonName=Previous button DatePickerAutomationPeer_LocalizedControlType=date picker DatePickerTextBox_DefaultWatermarkText= DatePicker_DropDownButtonName=Show Calendar DatePicker_WatermarkText=Select a date ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Exception Strings ; ; The following section is for all strings that will be used by exceptions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; DataGrid DataGrid_ColumnIndexOutOfRange=The index for the DataGridColumn with Header '{0}' is out of range. Index must be greater than or equal to 0 and less than Columns.Count. DataGrid_ColumnDisplayIndexOutOfRange=The DisplayIndex for the DataGridColumn with Header '{0}' is out of range. DisplayIndex must be greater than or equal to 0 and less than Columns.Count. DataGrid_DisplayIndexOutOfRange=The given DisplayIndex is out of range. DisplayIndex must be greater than or equal to 0 and less than Columns.Count. DataGrid_InvalidColumnReuse=DataGridColumn with Header '{0}' already exists in the Columns collection of a DataGrid. DataGrids cannot share columns and cannot contain duplicate column instances. DataGrid_DuplicateDisplayIndex=DisplayIndex is already used for other column. DisplayIndex should be unique per column. DataGrid_NewColumnInvalidDisplayIndex=Cannot add a column with an invalid DisplayIndex to the DataGrid.Columns collection. DataGrid_NullColumn=Cannot add a null column to a DataGrid's column collection. DataGrid_ReadonlyCellsItemsSource=The ItemsSource for the DataGridCellsPresenter is read-only. DataGrid_InvalidSortDescription=The SortDescriptions added are not valid. The probable solutions are to set the CanUserSort on the Column to false, or to use SortMemberPath property on the Column, or to handle the Sorting event on DataGrid. DataGrid_ProbableInvalidSortDescription=Items refresh of the DataGrid failed. One of the probable causes is that the SortDescriptions added are not valid, in which case probable solutions are to set the CanUserSort on the Column to false, or to use SortMemberPath property on the Column, or to handle the Sorting event on DataGrid. DataGrid_AutomationInvokeFailed=Invoke operation failed. Cannot edit another cell or row while the current one has validation errors. ; DataGrid Length DataGridLength_InvalidType=Invalid type. DataGridLength_Infinity=Value should not be infinity. ; DataGrid Selection DataGrid_CannotSelectCell=Cannot change cell selection when the SelectionUnit is FullRow. DataGridRow_CannotSelectRowWhenCells=The current value of the SelectionUnit property on the parent DataGrid prevents rows from being selected. ; SelectedCellsCollection SelectedCellsCollection_InvalidItem=The DataGridItem is invalid. SelectedCellsCollection_DuplicateItem=The collection already contains the item. ; VirtualizedCellInfoCollection VirtualizedCellInfoCollection_IsReadOnly=The collection cannot be modified. VirtualizedCellInfoCollection_DoesNotSupportIndexChanges=This collection does not support changing values with specific indexes. ; Copy ClipboardCopyMode_Disabled=Cannot perform copy if ClipboardCopyMode is None. ;Calendar Calendar_OnDisplayModePropertyChanged_InvalidValue=DisplayMode value is not valid. Calendar_OnFirstDayOfWeekChanged_InvalidValue=FirstDayOfWeek value is not valid. Calendar_OnSelectedDateChanged_InvalidValue=SelectedDate value is not valid. Calendar_OnSelectedDateChanged_InvalidOperation=The SelectedDate property cannot be set when the selection mode is None. CalendarCollection_MultiThreadedCollectionChangeNotSupported=This type of Collection does not support changes to its SourceCollection from a thread different from the Dispatcher thread. Calendar_CheckSelectionMode_InvalidOperation=The SelectedDates collection can be changed only in a multiple selection mode. Use the SelectedDate in a single selection mode. Calendar_OnSelectionModeChanged_InvalidValue=SelectionMode value is not valid. Calendar_UnSelectableDates=Value is not valid. ;DatePicker DatePickerTextBox_TemplatePartIsOfIncorrectType=The template part {0} is not an instance of {1}. DatePicker_OnSelectedDateFormatChanged_InvalidValue=DatePickerFormat value is not valid. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; UIAutomation strings ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CalendarAutomationPeer_MonthMode=Month CalendarAutomationPeer_YearMode=Year CalendarAutomationPeer_DecadeMode=Decade ================================================ FILE: WpfToolkit/Resources/SR.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Globalization; using System.Resources; namespace Microsoft.Windows.Controls { /// /// Retrieves exception strings and other localized strings. /// internal static class SR { internal static string Get(SRID id) { return _resourceManager.GetString(id.String); } internal static string Get(SRID id, params object[] args) { string message = _resourceManager.GetString(id.String); if (message != null) { // Apply arguments to formatted string (if applicable) if (args != null && args.Length > 0) { message = String.Format(CultureInfo.CurrentCulture, message, args); } } return message; } // Get exception string resources for current locale private static ResourceManager _resourceManager = new ResourceManager("ExceptionStringTable", typeof(SR).Assembly); } } ================================================ FILE: WpfToolkit/Resources/SRID.cs ================================================ //--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Globalization; using System.Resources; namespace Microsoft.Windows.Controls { // A wrapper around string identifiers. internal struct SRID { private string _string; public string String { get { return _string; } } private SRID(string s) { _string = s; } public static SRID DataGrid_SelectAllCommandText { get { return new SRID("DataGrid_SelectAllCommandText"); } } public static SRID DataGrid_SelectAllKey { get { return new SRID("DataGrid_SelectAllKey"); } } public static SRID DataGrid_SelectAllKeyDisplayString { get { return new SRID("DataGrid_SelectAllKeyDisplayString"); } } public static SRID DataGrid_BeginEditCommandText { get { return new SRID("DataGrid_BeginEditCommandText"); } } public static SRID DataGrid_CommitEditCommandText { get { return new SRID("DataGrid_CommitEditCommandText"); } } public static SRID DataGrid_CancelEditCommandText { get { return new SRID("DataGrid_CancelEditCommandText"); } } public static SRID DataGrid_DeleteCommandText { get { return new SRID("DataGrid_DeleteCommandText"); } } public static SRID DataGridCellItemAutomationPeer_NameCoreFormat { get { return new SRID("DataGridCellItemAutomationPeer_NameCoreFormat"); } } public static SRID CalendarAutomationPeer_CalendarButtonLocalizedControlType { get { return new SRID("CalendarAutomationPeer_CalendarButtonLocalizedControlType"); } } public static SRID CalendarAutomationPeer_DayButtonLocalizedControlType { get { return new SRID("CalendarAutomationPeer_DayButtonLocalizedControlType"); } } public static SRID CalendarAutomationPeer_BlackoutDayHelpText { get { return new SRID("CalendarAutomationPeer_BlackoutDayHelpText"); } } public static SRID Calendar_NextButtonName { get { return new SRID("Calendar_NextButtonName"); } } public static SRID Calendar_PreviousButtonName { get { return new SRID("Calendar_PreviousButtonName"); } } public static SRID DatePickerAutomationPeer_LocalizedControlType { get { return new SRID("DatePickerAutomationPeer_LocalizedControlType"); } } public static SRID DatePickerTextBox_DefaultWatermarkText { get { return new SRID("DatePickerTextBox_DefaultWatermarkText"); } } public static SRID DatePicker_DropDownButtonName { get { return new SRID("DatePicker_DropDownButtonName"); } } public static SRID DataGrid_ColumnIndexOutOfRange { get { return new SRID("DataGrid_ColumnIndexOutOfRange"); } } public static SRID DataGrid_ColumnDisplayIndexOutOfRange { get { return new SRID("DataGrid_ColumnDisplayIndexOutOfRange"); } } public static SRID DataGrid_DisplayIndexOutOfRange { get { return new SRID("DataGrid_DisplayIndexOutOfRange"); } } public static SRID DataGrid_InvalidColumnReuse { get { return new SRID("DataGrid_InvalidColumnReuse"); } } public static SRID DataGrid_DuplicateDisplayIndex { get { return new SRID("DataGrid_DuplicateDisplayIndex"); } } public static SRID DataGrid_NewColumnInvalidDisplayIndex { get { return new SRID("DataGrid_NewColumnInvalidDisplayIndex"); } } public static SRID DataGrid_NullColumn { get { return new SRID("DataGrid_NullColumn"); } } public static SRID DataGrid_ReadonlyCellsItemsSource { get { return new SRID("DataGrid_ReadonlyCellsItemsSource"); } } public static SRID DataGrid_InvalidSortDescription { get { return new SRID("DataGrid_InvalidSortDescription"); } } public static SRID DataGrid_ProbableInvalidSortDescription { get { return new SRID("DataGrid_ProbableInvalidSortDescription"); } } public static SRID DataGridLength_InvalidType { get { return new SRID("DataGridLength_InvalidType"); } } public static SRID DataGridLength_Infinity { get { return new SRID("DataGridLength_Infinity"); } } public static SRID DataGrid_CannotSelectCell { get { return new SRID("DataGrid_CannotSelectCell"); } } public static SRID DataGridRow_CannotSelectRowWhenCells { get { return new SRID("DataGridRow_CannotSelectRowWhenCells"); } } public static SRID DataGrid_AutomationInvokeFailed { get { return new SRID("DataGrid_AutomationInvokeFailed"); } } public static SRID SelectedCellsCollection_InvalidItem { get { return new SRID("SelectedCellsCollection_InvalidItem"); } } public static SRID SelectedCellsCollection_DuplicateItem { get { return new SRID("SelectedCellsCollection_DuplicateItem"); } } public static SRID VirtualizedCellInfoCollection_IsReadOnly { get { return new SRID("VirtualizedCellInfoCollection_IsReadOnly"); } } public static SRID VirtualizedCellInfoCollection_DoesNotSupportIndexChanges { get { return new SRID("VirtualizedCellInfoCollection_DoesNotSupportIndexChanges"); } } public static SRID ClipboardCopyMode_Disabled { get { return new SRID("ClipboardCopyMode_Disabled"); } } public static SRID Calendar_OnDisplayModePropertyChanged_InvalidValue { get { return new SRID("Calendar_OnDisplayModePropertyChanged_InvalidValue"); } } public static SRID Calendar_OnFirstDayOfWeekChanged_InvalidValue { get { return new SRID("Calendar_OnFirstDayOfWeekChanged_InvalidValue"); } } public static SRID Calendar_OnSelectedDateChanged_InvalidValue { get { return new SRID("Calendar_OnSelectedDateChanged_InvalidValue"); } } public static SRID Calendar_OnSelectedDateChanged_InvalidOperation { get { return new SRID("Calendar_OnSelectedDateChanged_InvalidOperation"); } } public static SRID CalendarCollection_MultiThreadedCollectionChangeNotSupported { get { return new SRID("CalendarCollection_MultiThreadedCollectionChangeNotSupported"); } } public static SRID Calendar_CheckSelectionMode_InvalidOperation { get { return new SRID("Calendar_CheckSelectionMode_InvalidOperation"); } } public static SRID Calendar_OnSelectionModeChanged_InvalidValue { get { return new SRID("Calendar_OnSelectionModeChanged_InvalidValue"); } } public static SRID Calendar_UnSelectableDates { get { return new SRID("Calendar_UnSelectableDates"); } } public static SRID DatePickerTextBox_TemplatePartIsOfIncorrectType { get { return new SRID("DatePickerTextBox_TemplatePartIsOfIncorrectType"); } } public static SRID DatePicker_OnSelectedDateFormatChanged_InvalidValue { get { return new SRID("DatePicker_OnSelectedDateFormatChanged_InvalidValue"); } } public static SRID DatePicker_WatermarkText { get { return new SRID("DatePicker_WatermarkText"); } } public static SRID CalendarAutomationPeer_MonthMode { get { return new SRID("CalendarAutomationPeer_MonthMode"); } } public static SRID CalendarAutomationPeer_YearMode { get { return new SRID("CalendarAutomationPeer_YearMode"); } } public static SRID CalendarAutomationPeer_DecadeMode { get { return new SRID("CalendarAutomationPeer_DecadeMode"); } } } } ================================================ FILE: WpfToolkit/Samples/Accordion/AccordionSample.xaml ================================================  regular string item 3 ================================================ FILE: WpfToolkit/Samples/Accordion/AccordionSample.xaml.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Data; using System.Globalization; namespace System.Windows.Controls.Samples { /// /// Sample application for Accordion. /// #if SILVERLIGHT [Sample("Accordion Playaround sample", DifficultyLevel.Basic)] #endif [Category("Accordion")] public partial class AccordionSample : UserControl { /// /// Initializes a new instance of the class. /// public AccordionSample() { InitializeComponent(); icSelectedIndices.SetBinding( ItemsControl.ItemsSourceProperty, new Binding("SelectedIndices") { Source = acc, Mode = BindingMode.OneWay }); tbSelectedIndex.SetBinding( TextBox.TextProperty, new Binding("SelectedIndex") { Source = acc, Mode = BindingMode.TwoWay }); acc.SetBinding( Accordion.SelectedIndexProperty, new Binding("Index") { Source = this, Mode = BindingMode.TwoWay }); cbSelectionMode.SelectedItem = cbSelectionMode.Items .OfType() .FirstOrDefault(item => item.Content.Equals(acc.SelectionMode.ToString())); cbExpandDirection.SelectedItem = cbExpandDirection.Items .OfType() .FirstOrDefault(item => item.Content.Equals(acc.ExpandDirection.ToString())); cbSelectionSequence.SelectedItem = cbSelectionSequence.Items .OfType() .FirstOrDefault(item => item.Content.Equals(acc.SelectionSequence.ToString())); } /// /// Gets or sets the index. /// /// The index. public int Index { get { return index; } set { index = value; tbSelectedIndex.Text = value.ToString(CultureInfo.InvariantCulture); } } /// /// Backingfield for Index. /// private int index; /// /// Expands the direction changed. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void ExpandDirectionChanged(object sender, SelectionChangedEventArgs e) { acc.ExpandDirection = (ExpandDirection)Enum.Parse( typeof(ExpandDirection), ((ComboBoxItem)cbExpandDirection.SelectedItem).Content.ToString(), true); } /// /// Sets the height. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void SetHeight(object sender, System.Windows.RoutedEventArgs e) { acc.Height = 500; } /// /// Removes the height. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void RemoveHeight(object sender, System.Windows.RoutedEventArgs e) { acc.ClearValue(Control.HeightProperty); } /// /// Selections the mode changed. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void SelectionModeChanged(object sender, SelectionChangedEventArgs e) { acc.SelectionMode = (AccordionSelectionMode)Enum.Parse( typeof(AccordionSelectionMode), ((ComboBoxItem)cbSelectionMode.SelectedItem).Content.ToString(), true); } /// /// Selects all. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void SelectAll(object sender, RoutedEventArgs e) { acc.SelectAll(); } /// /// Unselects all. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void UnselectAll(object sender, RoutedEventArgs e) { acc.UnselectAll(); } /// /// React to selectionSequence event. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked up in Xaml.")] private void SelectionSequenceChanged(object sender, SelectionChangedEventArgs e) { acc.SelectionSequence = (SelectionSequence)Enum.Parse( typeof(SelectionSequence), ((ComboBoxItem)cbSelectionSequence.SelectedItem).Content.ToString(), true); } /// /// Removes the width. /// /// The sender. /// The instance containing the event data. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by Xaml.")] private void RemoveWidth(object sender, RoutedEventArgs e) { acc.ClearValue(Control.WidthProperty); } /// /// Sets the width. /// /// The sender. /// The instance containing the event data. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by Xaml.")] private void SetWidth(object sender, RoutedEventArgs e) { acc.Width = 300; } private void Button_OnClick(object sender, RoutedEventArgs e) { button.Height += 50; } } } ================================================ FILE: WpfToolkit/Samples/Accordion/AccordionShowcase.xaml ================================================  Visible Visible False True The Accordion is a control that allows you to provide multiple panes and display them. There are several selection modes, so you can decide if only one pane can be open, or multiple. It is also possible to adjust the order in which the panes open and close: Simultaneous or close first. The Accordion is implemented as an itemscontrol that contains AccordionItem controls. Each AccordionItem control has a template for its Header and its Content. It also supports filling to a specific height or width. To do so, either set a Height (or Width) or use a VerticalAlignment (or HorizontalAlignment) of 'Stretch'. The Accordion will now always fill to that space. If more than one pane may be opened, space is divided equally among them. Whether you fill to height or width is determined by the ExpandDirection property. An accordion will is able to expand to Down, Up, Left and Right directions. The accordion uses AccordionItems that can be fully templated. In order to 'reveal' contents slowly, an ExpandableContentControl is used. That control has a percentage property that can be animated. A value of 1 means that the content is completely shown. AccordionItem animates this property using a keyspline to get a nice easing-in effect. You can retemplate AccordionItem and create your own transitions! Visible False True * * 1 0 * * 1 1 2 2 1 0 * * 1 1 2 2 0 1 * * The Accordion is a control that allows you to provide multiple panes and display them. There are several selection modes, so you can decide if only one pane can be open, or multiple. It is also possible to adjust the order in which the panes open and close: Simultaneous or close first. The Accordion is implemented as an itemscontrol that contains AccordionItem controls. Each AccordionItem control has a template for its Header and its Content. It also supports filling to a specific height or width. To do so, either set a Height (or Width) or use a VerticalAlignment (or HorizontalAlignment) of 'Stretch'. The Accordion will now always fill to that space. If more than one pane may be opened, space is divided equally among them. Whether you fill to height or width is determined by the ExpandDirection property. An accordion will is able to expand to Down, Up, Left and Right directions. The accordion uses AccordionItems that can be fully templated. In order to 'reveal' contents slowly, an ExpandableContentControl is used. That control has a percentage property that can be animated. A value of 1 means that the content is completely shown. AccordionItem animates this property using a keyspline to get a nice easing-in effect. You can retemplate AccordionItem and create your own transitions! ================================================ FILE: WpfToolkit/Samples/Accordion/AccordionShowcase.xaml.cs ================================================ // (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993] for details. // All other rights reserved. using System.ComponentModel; namespace System.Windows.Controls.Samples { /// /// Two samples that completely restyles accordion. /// #if SILVERLIGHT [Sample("Accordion Showcase", DifficultyLevel.Basic)] #endif [Category("Accordion")] public partial class AccordionShowcase : UserControl { /// /// Initializes a new instance of the /// class. /// public AccordionShowcase() { InitializeComponent(); } } } ================================================ FILE: WpfToolkit/Samples/Accordion/AccordionUsage.xaml ================================================