如何在WPF Toolkit的图表控件中实现可命令的ColumnSeries

时间:2009-07-06 14:07:22

标签: wpf charts prism wpftoolkit

我需要能够指定在SelectionChanged事件触发时运行的命令。我已经知道如何实现ICommandSource接口;我需要知道的是如何在列系列中添加一个命令来处理SelectionChanged事件。

当我从ColumnBarBaseSeries< ...>继承时基类我必须覆盖GetAxes()和UpdateDatePoint(),我不知道如何实现。

3 个答案:

答案 0 :(得分:2)

您可以使用附加行为来解决此问题。

创建一个SelectionChangedBehaviour,它将selectionChanged事件连接到您正在附加行为的元素,然后您可以将任何ICommand绑定到该行为。

有关附加行为的更多信息 -

希望有所帮助

答案 1 :(得分:1)

以下是一些代码,用于为ColumnSeries为SelectionChanged命令添加附加行为。

public static class ColumnSeriesBehavior
{
    private static DelegateCommand<object> SelectionChangedCommand;       

    public static DelegateCommand<object> GetSelectionChangedCommand(ColumnSeries cs)
    {
        return cs.GetValue(SelectionChangedCommandProperty) as DelegateCommand<object>;
    }

    public static void SetSelectionChangedCommand(ColumnSeries cs, DelegateCommand<object> value)
    {
        cs.SetValue(SelectionChangedCommandProperty, value);
    }

    // Using a DependencyProperty as the backing store for SelectionChangedCommand.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectionChangedCommandProperty =
        DependencyProperty.RegisterAttached("SelectionChangedCommand", typeof(DelegateCommand<object>), typeof(ColumnSeriesBehavior), new UIPropertyMetadata(null, OnSelectionChangedCommandChanged));

    private static void OnSelectionChangedCommandChanged(
        DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        ColumnSeries item = depObj as ColumnSeries;
        if (item == null)
        {                
            return;
        }
        if (e.NewValue is DelegateCommand<object> == false)
        {

            return;
        }

        SelectionChangedCommand = e.NewValue as DelegateCommand<object>;
        item.SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(Column_SelectionChanged);
    }

    private static void Column_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        if (SelectionChangedCommand != null)
            SelectionChangedCommand.Execute(sender);
    }

}

在XAML中附加属性:

<chartingToolkit:Chart.Series>
            <chartingToolkit:ColumnSeries
                IsSelectionEnabled="True"                                        
                ItemsSource="{Binding YourItemSource}"
                IndependentValueBinding="{Binding YourIndValue, Path=YourIndValuePath}"
                DependentValueBinding="{Binding YourDepValue, Path=YourDepValuePath}"                                        
                >
                <chartingToolkit:ColumnSeries.Style>
                    <Style>
                        <!-- Attaching the SelectionChangedCommand behavior -->
                        <Setter Property="local:ColumnSeriesBehavior.SelectionChangedCommand"
                                Value="{Binding YourDelegateCommand}"/>
                    </Style>
                </chartingToolkit:ColumnSeries.Style>
            </chartingToolkit:ColumnSeries>
        </chartingToolkit:Chart.Series>

答案 2 :(得分:0)

以下是一些似乎对我来说实现自己的CommandColumnSeries的代码,我从ColumnSeries Sealed Class的源代码中偷了很多代码:

public class CommandColumnSeries : ColumnBarBaseSeries<ColumnDataPoint>
{
    #region "ICommandSource"

    [Localizability(LocalizationCategory.NeverLocalize), Category("Action"), Bindable(true)]
    public ICommand Command
    {
        get
        {
            return (ICommand)base.GetValue(CommandProperty);
        }
        set
        {
            base.SetValue(CommandProperty, value);
        }
    }

    [Bindable(true), Category("Action"), Localizability(LocalizationCategory.NeverLocalize)]
    public object CommandParameter
    {
        get
        {
            return base.GetValue(CommandParameterProperty);
        }
        set
        {
            base.SetValue(CommandParameterProperty, value);
        }
    }

    [Category("Action"), Bindable(true)]
    public IInputElement CommandTarget
    {
        get
        {
            return (IInputElement)base.GetValue(CommandTargetProperty);
        }
        set
        {
            base.SetValue(CommandTargetProperty, value);
        }
    }

    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(CommandColumnSeries), new FrameworkPropertyMetadata(null));
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandColumnSeries), new FrameworkPropertyMetadata(null));
    public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CommandColumnSeries), new FrameworkPropertyMetadata(null));

    #endregion

    #region public IRangeAxis DependentRangeAxis

    /// <summary>
    /// Gets or sets the dependent range axis.
    /// </summary>
    public IRangeAxis DependentRangeAxis
    {
        get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; }
        set { SetValue(DependentRangeAxisProperty, value); }
    }

    /// <summary>
    /// Identifies the DependentRangeAxis dependency property.
    /// </summary>
    [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because the base classes need to share this dependency property.")]
    public static readonly DependencyProperty DependentRangeAxisProperty =
        DependencyProperty.Register(
            "DependentRangeAxis",
            typeof(IRangeAxis),
            typeof(ColumnSeries),
            new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged));

    /// <summary>
    /// DependentRangeAxisProperty property changed handler.
    /// </summary>
    /// <param name="d">ColumnBarBaseSeries that changed its DependentRangeAxis.</param>
    /// <param name="e">Event arguments.</param>
    private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        CommandColumnSeries source = (CommandColumnSeries)d;
        IRangeAxis newValue = (IRangeAxis)e.NewValue;
        source.OnDependentRangeAxisPropertyChanged(newValue);
    }

    /// <summary>
    /// DependentRangeAxisProperty property changed handler.
    /// </summary>
    /// <param name="newValue">New value.</param>
    private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue)
    {
        this.InternalDependentAxis = (IAxis)newValue;
    }
    #endregion public IRangeAxis DependentRangeAxis

    #region public IAxis IndependentAxis
    /// <summary>
    /// Gets or sets the independent category axis.
    /// </summary>
    public IAxis IndependentAxis
    {
        get { return GetValue(IndependentAxisProperty) as IAxis; }
        set { SetValue(IndependentAxisProperty, value); }
    }

    /// <summary>
    /// Identifies the IndependentAxis dependency property.
    /// </summary>
    [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because the base classes need to share this dependency property.")]
    public static readonly DependencyProperty IndependentAxisProperty =
        DependencyProperty.Register(
            "IndependentAxis",
            typeof(IAxis),
            typeof(ColumnSeries),
            new PropertyMetadata(null, OnIndependentAxisPropertyChanged));

    /// <summary>
    /// IndependentAxisProperty property changed handler.
    /// </summary>
    /// <param name="d">ColumnBarBaseSeries that changed its IndependentAxis.</param>
    /// <param name="e">Event arguments.</param>
    private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        CommandColumnSeries source = (CommandColumnSeries)d;
        IAxis newValue = (IAxis)e.NewValue;
        source.OnIndependentAxisPropertyChanged(newValue);
    }

    /// <summary>
    /// IndependentAxisProperty property changed handler.
    /// </summary>
    /// <param name="newValue">New value.</param>
    private void OnIndependentAxisPropertyChanged(IAxis newValue)
    {
        this.InternalIndependentAxis = (IAxis)newValue;
    }
    #endregion public IAxis IndependentAxis



    public CommandColumnSeries()
    {
        this.SelectionChanged += new SelectionChangedEventHandler(CommandColumnSeries_SelectionChanged);
    }

    private void CommandColumnSeries_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {   
        if (Command != null)
        {
            RoutedCommand routedCommand = Command as RoutedCommand;
            CommandParameter = e.Source;

            if (routedCommand != null)
            {
                routedCommand.Execute(CommandParameter, CommandTarget);
            }
            else
            {
                Command.Execute(CommandParameter);
            }
        }
    }

    protected override void GetAxes(DataPoint firstDataPoint)
    {
        // Taken from the source of the ColumnSeries sealed class.
        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("No Suitable Axes found for plotting range axis.");
                }
                DisplayAxis axis = rangeAxis as DisplayAxis;
                if (axis != null)
                {
                    axis.ShowGridLines = true;
                }
                return rangeAxis;
            });

    }

    protected override void UpdateDataPoint(DataPoint dataPoint)
    {
        // This code taken from the ColumnSeries sealed class.
        if (SeriesHost == null )//|| PlotArea == null)
        {
            return;
        }

        object category = dataPoint.ActualIndependentValue ?? (IndexOf<DataPoint>(this.ActiveDataPoints, dataPoint) + 1);
        Range<UnitValue> coordinateRange = GetCategoryRange(category);
        if (!coordinateRange.HasData)
        {
            return;
        }
        else if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels)
        {
            throw new InvalidOperationException("This Series Does Not Support Radial Axes");
        }

        double minimum = (double)coordinateRange.Minimum.Value;
        double maximum = (double)coordinateRange.Maximum.Value;

        double plotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value.Value;
        IEnumerable<CommandColumnSeries> columnSeries = SeriesHost.Series.OfType<CommandColumnSeries>().Where(series => series.ActualIndependentAxis == ActualIndependentAxis);
        int numberOfSeries = columnSeries.Count();
        double coordinateRangeWidth = (maximum - minimum);
        double segmentWidth = coordinateRangeWidth * 0.8;
        double columnWidth = segmentWidth / numberOfSeries;
        int seriesIndex = IndexOf<CommandColumnSeries>(columnSeries, this);

        double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ToDouble(dataPoint.ActualDependentValue)).Value.Value;
        double zeroPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value.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<object, DataPoint> categoryGrouping = GetDataPointGroup(category);
            int index = GroupIndexOf(categoryGrouping, dataPoint);
            dataPointX += (index * (columnWidth * 0.2)) / (categoryGrouping.Count() - 1);
            columnWidth *= 0.8;
            Canvas.SetZIndex(dataPoint, -index);
        }

        if (CanGraph(dataPointY) && CanGraph(dataPointX) && CanGraph(zeroPointY))
        {
            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;
        }
    }

    private static int IndexOf<T>(IEnumerable<T> collection, T target)
    {
        int i = 0;
        foreach (var obj in collection)
        {
            if (obj.Equals(target))
                return i;
            i++;
        }
        return -1;
    }

    private static int GroupIndexOf(IGrouping<object, DataPoint> group, DataPoint point)
    {
        int i = 0;
        foreach (var pt in group)
        {
            if (pt == point)
                return i;
            i++;
        }

        return -1;
    }

    /// <summary>
    /// Returns a value indicating whether this value can be graphed on a 
    /// linear axis.
    /// </summary>
    /// <param name="value">The value to evaluate.</param>
    /// <returns>A value indicating whether this value can be graphed on a 
    /// linear axis.</returns>
    private static bool CanGraph(double value)
    {
        return !double.IsNaN(value) && !double.IsNegativeInfinity(value) && !double.IsPositiveInfinity(value) && !double.IsInfinity(value);
    }

    /// <summary>
    /// Converts an object into a double.
    /// </summary>
    /// <param name="value">The value to convert to a double.</param>
    /// <returns>The converted double value.</returns>
    private static double ToDouble(object value)
    {
        return Convert.ToDouble(value, CultureInfo.InvariantCulture);
    }


}