Search code examples
wpfcheckboxdynamiclivecharts

How can I WPF bind dynamically created checkboxes to dynamically created series in LiveCharts?


I have a liveChart and am creating checkboxes for each item in a list. This list also has data for each series in liveCharts. How do I bind my dynamically created checkboxes with each individual LiveCharts.LineSeries from my data?

I've created the checkboxes:

<!-- Creating checkboxes by binding to list -->
<ListView ItemsSource="{Binding ElementItemList}" 
ScrollViewer.HorizontalScrollBarVisibility="Disabled" Width="600">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>

            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=ElementName}" />
                        <CheckBox IsChecked="{Binding Path=ElementIsSelected}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>

        </ListView>

<!-- Display the chart -->
<Grid Grid.Row="1" x:Name="TestGrid"></Grid>

Solution

  • So I assume that you want to have a CheckBox representing each LineSeries in your SeriesCollection.

    So I would have two public properties, one for the SeriesCollection and the other for the list of CheckBox controls.

    public SeriesCollection SeriesCollection { get; set; }
    public List<CheckBox> CheckBoxCollection { get; set; }
    

    Then following is a function that mimics dynamically creating your LineSeries and CheckBox controls since you didn't provide that code. It is important to have some sort of a connection between the CheckBox controls and your line series, and in this case I decided to set LineSeries.Title and CheckBox.Name the same.

    Also note that in order to have the CheckBox do something upon checking/unchecking, you'd need to register two events for each.

    public void DynamicallyCreateStuff()
    {
        SeriesCollection = new SeriesCollection();
        CheckBoxCollection = new List<CheckBox>();
    
        var count = 3;
        var val1 = new List<double>() { 1, 2, 3 };
        var val2 = new List<double>() { 9, 5, 3 };
        var val3 = new List<double>() { 1, 4, 9 };
    
        for (int i = 1; i <= count; i++)
        {
            var name = string.Format("LineSeries{0}", i);
    
            var checkBox = new CheckBox
            {
                Name = name,
                Content = name,
                Margin = new Thickness() { Left = 8, Top = 8, Right = 8, Bottom = 8 },
                IsChecked = true
            };
            checkBox.Checked += DynamicCheckBoxChecked;
            checkBox.Unchecked += DynamicCheckBoxUnchecked;
            CheckBoxCollection.Add(checkBox);
    
            var lineSeries = new LineSeries
            {
                Title = name
            };
            if (i == 1)
            {
                lineSeries.Values = new ChartValues<double>(val1);
            }
            else if (i == 2)
            {
                lineSeries.Values = new ChartValues<double>(val2);
            }
            else if (i == 3)
            {
                lineSeries.Values = new ChartValues<double>(val3);
            }
    
            SeriesCollection.Add(lineSeries);
        }
    }
    

    In my case, I decided to have the corresponding series become visible/hidden upon clicking the CheckBox, so my check/uncheck methods look like this:

    private void DynamicCheckBoxChecked(object sender, EventArgs e)
    {
        ShowHideSeries(sender, Visibility.Visible);
    }
    
    private void DynamicCheckBoxUnchecked(object sender, EventArgs e)
    {
        ShowHideSeries(sender, Visibility.Collapsed);
    }
    
    private void ShowHideSeries(object sender, Visibility visibility)
    {
        var checkBox = (CheckBox)sender;
        var found = SeriesCollection.FirstOrDefault(x => x.Title == checkBox.Name);
        if (found != null)
        {
            var series = (LineSeries)found;
            series.Visibility = visibility;
        }
    }
    

    I didn't use a ViewModel in order to save time and for the sake of simplicity, so my MainWindow constructor looks like this:

    public MainWindow()
    {
        InitializeComponent();
        DynamicallyCreateStuff();
        DataContext = this;
    }
    

    And XAML is pretty bare bones here:

    <Window x:Class="SOLineCharts.MainWindow"
            ....
            xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
            mc:Ignorable="d"
            WindowStartupLocation="CenterScreen"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <ItemsControl Grid.Column="0" 
                          ItemsSource="{Binding CheckBoxCollection}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Vertical" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
            <lvc:CartesianChart Series="{Binding SeriesCollection}" Grid.Column="1"/>
        </Grid>
    </Window>
    

    Result:

    Upon loading:

    enter image description here

    Unchecking one check box:

    enter image description here