简体   繁体   中英

Looping through colors in binding

I'm working on a large code project that was written by someone else, so I'm trying to add a change with as little impact as possible. It uses System.Windows.Controls.DataVisualization.Toolkit to create a linechart with a number of LineSeries combined into one MultiChart , which is a class my predecessor created himself.

The lines are all the same color. I was asked to make them different colors. Or more than one color really. The number of lines is not constant, so I was hoping to loop through a list of colors. Then all I would have to do is make a large enough to cover most cases.

Because of the inconsistency in line number, I can't explicitly define each color. In addition, the MultiChart has a SeriesSource property, which is an ObservableCollection of multiple LineSeries which is in turn an ObservableCollection of data points, that is set via binding to the ViewModel.

So I think I'll have to bind the color to a value outside of the xaml. Since it's view related work, I imagine the code behind would be a valid location for my color changing, but I've been unable to find a solution that works there.

I've looked at a number of question on SO and elsewhere and none of them quite worked, with or without consideration for the MVVM pattern. I'm just looking for a working solution that I can rework into MVVM, but bonus points if it's good practice as well.

I have been working with WPF for a while now, and for some reason I can't fully wrap my head around data binding, which is likely why I've been unsuccessful with most of the solutions I found. Despite that I think I got close with this:

In <UserControl.Resources>

<local:LocalColorConverter x:Key="MyColorConverter"/>

<Style x:Key="dataPointStyle" TargetType="{x:Type charting:LineDataPoint}">
        <Setter Property="Background" Value="{Binding Path=DataContext.ColorCount, 
                                              RelativeSource={RelativeSource AncestorType=local:MultiChart, Mode=FindAncestor}, 
                                              Converter={StaticResource MyColorConverter}}"/>
</Style>

In LocalColorConverter.cs :

class LocalColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int count = (int)value;
        //SolidColorBrush returnBrush = new SolidColorBrush();
        SolidColorBrush color = new SolidColorBrush();
        switch (count % 5)
        {
            case 0:
                color.Color = Colors.Blue;
                break;
            case 1:
                color.Color = Colors.Green;
                break;
            case 2:
                color.Color = Colors.Red;
                break;
            case 3:
                color.Color = Colors.Purple;
                break;
            case 4:
                color.Color = Colors.Yellow;
                break;
        }
        return color;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I realize this is not very good in practice. It relies on a ColorCount value from the viewmodel, which is information relevant to the view. Bad MVVM, but I'm just trying to get it working before I make it fit the pattern.

In addition, it doesn't really work as is, since ColorCount is never changed. It's initially set to 0 in the viewmodel. If I were to continue down this path, I would like to increment the value of ColorCount so that the colors would change each time the Converter is used.

I tried just doing ((int)value)++ before the Converter returns, but that didn't work. I didn't think it would, but it gives you an idea of what I'm looking for.

Otherwise, I think my best solution is to do this operation in the code behind. But I don't know where/how to do it. It would have to change the color when the UserControl gets to a new LineSeries . The current code behind has a LineSeries_Loaded event handler implemented, but the color isn't set in the line, it's set for each point in the line. So I need to increment my color counter at each line, then use the count at each point to determine it's color.

I've tried this, but I can't seem to find the Background property this way. Note that chart is the name given to the MultiChart object.

private void LineSeries_Loaded(object sender, RoutedEventArgs e)
    {
        foreach(LineSeries line in chart.Series.Cast<LineSeries>())
        {
            foreach(LineDataPoint point in line)
            {

            }
        }
    }

But I get the error that line has no public GetEnumerator.

Any suggestions?

Well, looks like writing the question got my creative juices flowing. I remembered I tried to set the style setter in the code behind, but you can't modify the style at the loaded time. But I just realized I can switch styles at any time. So I made these:

<Style x:Key="blueDataPointStyle" TargetType="{x:Type charting:LineDataPoint}" BasedOn="{StaticResource dataPointStyle}">
    <Setter Property="Background" Value="Blue"/>
</Style>

<Style x:Key="redDataPointStyle" TargetType="{x:Type charting:LineDataPoint}" BasedOn="{StaticResource dataPointStyle}">
    <Setter Property="Background" Value="Red"/>
</Style>

<Style x:Key="greenDataPointStyle" TargetType="{x:Type charting:LineDataPoint}" BasedOn="{StaticResource dataPointStyle}">
    <Setter Property="Background" Value="Green"/>
</Style>

Then, in the code behind:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        int count = 0;
        foreach (LineSeries line in chart.Series)
        {
            switch (count % 3)
            {
                case 0:
                    line.DataPointStyle = Resources["blueDataPointStyle"] as Style;
                    break;
                case 1:
                    line.DataPointStyle = Resources["redDataPointStyle"] as Style;
                    break;
                case 2:
                    line.DataPointStyle = Resources["greenDataPointStyle"] as Style;
                    break;
            }
            count++;

        }
    }

It's working! Well mostly, turns out every other line is grouped into one, so I still have some work to do. But this is the answer for what I asked. Plus it seems to fit MVVM. Turns out I don't need binding(probably why I was able to come up with it). But I am still interested in other ideas. I would like a proper binding example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM