2

I'm going to display the list of data using listview, the data class contain the following elements:

class MyData
{
    static Random rnd = new Random();
    public int id { get; set; }
    public string name { get; set; }
    public bool selected { get; set; }

    private LED[] LEDs = new LED[21];
    private byte[] servos = new byte[21];
}

And it is expected to display the data in table format as below,

{selected} {id} {name} {servo[1]} {servo[2]} ......

(servo[0] should be hidden)

with the layout of each column:

- {selected] : checkbox
- {id} : right alignment
- {name] : left alignment
- {servo} : right alignment, with background based on LED setting

I have build the ListView as below

    <ListView x:Name="lvData" FontSize="13" Background="LightGoldenrodYellow" Margin="0,0,0,0" >
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            </Style>
        </ListView.ItemContainerStyle>            
        <ListView.View>
            <GridView>

                <GridViewColumn >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding selected}" Checked="Selection_Changed" Unchecked="Selection_Changed" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="id" Width="60">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate >
                            <TextBlock Text="{Binding id}" TextAlignment="Right" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn Header="name" Width="100" DisplayMemberBinding="{Binding name}"/>

                <GridViewColumn Header="s01" Width="35">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid Background="{Binding Path=LED01}" Margin="-5,0,-5,0">
                                <Label Margin="0,0,0,0" Content="{Binding Path=S01}" HorizontalContentAlignment="Right"></Label>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn Header="s02" Width="35">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid Background="{Binding Path=LED02}" Margin="-5,0,-5,0">
                                <Label Margin="0,0,0,0" Content="{Binding Path=S01}" HorizontalContentAlignment="Right"></Label>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                :
                {repeat for s03...s20}
                :

            </GridView>
        </ListView.View>
    </ListView>

With some properties added to my data class:

        public string LED01 { get { return GetLED(1); } }
        public string S01 { get { return GetServo(1); } }

        public string LED02 { get { return GetLED(2); } }
        public string S02 { get { return GetServo(2); } }

And two methods GetLED & GetServo return the color & display based on the value of array LEDs & servos with given index.

It seems really dummy that I have repeated the GridViewColumn block many times, and 40 dummy properties have been created as it cannot bind to the method directly.

May I know if there has any simple way to build the listview?

In fact, the size of array is not fix, 21 is just the maximum size.

I'd like to know if it can build the column dynamically, so that it can be created based on the actual size, and those repeated columns can be done within a loop. And set the Background & Data content based on class method, so that I can set to GetLED(?) & GetServo(?) directly without building a dummy property for it.

Thanks in advance.

7
  • Size of LEDs and Servos will always be same, correct? Commented May 14, 2018 at 3:13
  • Yes, it's going to build a class for them, so it will become an array of servo class with LED. But in existing program they are in two array. Commented May 14, 2018 at 3:28
  • ok. I am thinking on the solution for this. I thought of an approach but need some time to tell you. Commented May 14, 2018 at 3:29
  • Is it feasible for you if LED and Servo columns are empty for some rows? e.g.: for id = 1 there are 10 LED and Servos, But for id=2 there 15. Then in ListView , 5 columns will show empty for row where id = 1. Commented May 14, 2018 at 3:36
  • 1
    I suppose easiest way might be to build those columns in code, not in xaml. Commented May 14, 2018 at 6:45

1 Answer 1

1

Your question is little bit lengthy and logical. I tried to achieve the functionality you need.

Kindly go throw following:

MODELS

I divided LED and Servo detail to a different class and took List object of that class into MyData class.

class MyData
{
    public int id { get; set; }
    public string name { get; set; }
    public bool selected { get; set; }
    public List<MySubData> lstSubData { get; set; }
}

class MySubData
{
    public string LED;
    public string Servo;
}

Converters

We need two converters during this process.

In ColorConverter, you need to define your condition for generating different colors based on LED value.

public class TextConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return System.Convert.ToString(value).Split('#').First();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return new NotImplementedException();
    }
}

public class ColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    var val = System.Convert.ToString(value).Split('#').Last();

        //YOUR COLOR CONDITIONS HERE. I AM RETURNING SIMPLE ONE COLOR

        return new SolidColorBrush(Colors.Red);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return new NotImplementedException();
    }
}

XAML

<Window x:Class="WPFTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFTest"
        mc:Ignorable="d"
        Title="TestWPF" Height="300" Width="400" 
        WindowStyle="SingleBorderWindow" 
        WindowStartupLocation="CenterScreen">

    <Grid>
        <ListView x:Name="lvData" FontSize="13" Background="LightGoldenrodYellow" Margin="0,0,0,0" >

            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
</Window>

CodeBehind

The main logic I applied is in code behind (C#). I generate ListView.View in CS page only. This code will generate dynamic GridView and DataTable for both UI and data.

I took sample data to simulate your requirement. You can take actual data. I took a variable globalServo for defining setting (as you mentioned in comments) for number of columns.

public partial class MainWindow : Window
{
    public int globalServo = 21;

    public MainWindow()
    {
        InitializeComponent();

        List<MyData> lstMyData = new List<MyData>();

        for (int i = 1; i < 6; i++)
        {
            List<MySubData> lstMySubData = new List<MySubData>();

            for (int j = 0; j < globalServo; j++)
            {
                lstMySubData.Add(new MySubData() { LED = "LED" + j, Servo = "Servo" + j });
            }

            lstMyData.Add(new MyData()
            {
                id = i,
                name = "name-" + i,
                selected = Convert.ToBoolean(i % 2),
                lstSubData = lstMySubData
            });
        }

        lvData.View = GenerateGridView();
        lvData.ItemsSource = GenerateSource(lstMyData).DefaultView;
    }

    private GridView GenerateGridView()
    {
        GridView view = new GridView();

        view.Columns.Add(new GridViewColumn() { Header = "Id", DisplayMemberBinding = new Binding("Id") });
        view.Columns.Add(new GridViewColumn() { Header = "Name", DisplayMemberBinding = new Binding("Name") });
        view.Columns.Add(new GridViewColumn() { Header = "Selected", CellTemplate = GetCheckboxTemplate() });

        for (int i = 1; i <= globalServo; i++)
        {
            view.Columns.Add(new GridViewColumn() { Header = "Servo" + i, CellTemplate = GetTextBlockTemplate(i) });
        }

        return view;
    }

    private DataTable GenerateSource(List<MyData> dataList)
    {
        DataTable dt = new DataTable();
        dt.Columns.Add("Id");
        dt.Columns.Add("Name");
        dt.Columns.Add("Selected");

        for (int i = 1; i <= globalServo; i++)
        {
            dt.Columns.Add("Servo" + i);
        }

        foreach (var item in dataList)
        {
            DataRow row = dt.NewRow();
            row["Id"] = item.id;
            row["Name"] = item.name;
            row["Selected"] = item.selected;

            for (int i = 1; i <= globalServo; i++)
            {
                row["Servo" + i] = item.lstSubData[i - 1].Servo + "##" + item.lstSubData[i - 1].LED;
            }

            dt.Rows.Add(row);
        }

        return dt;
    }

    private DataTemplate GetCheckboxTemplate()
    {
        DataTemplate dt = new DataTemplate(typeof(CheckBox));
        FrameworkElementFactory chkElement = new FrameworkElementFactory(typeof(CheckBox));
        dt.VisualTree = chkElement;

        Binding bind = new Binding();
        bind.Path = new PropertyPath("Selected");
        chkElement.SetBinding(CheckBox.IsCheckedProperty, bind);

        return dt;
    }

    private DataTemplate GetTextBlockTemplate(int Index)
    {
        TextConverter textConverter = new TextConverter();
        ColorConverter colorConverter = new ColorConverter();

        DataTemplate dt = new DataTemplate(typeof(TextBlock));
        FrameworkElementFactory txtElement = new FrameworkElementFactory(typeof(TextBlock));
        dt.VisualTree = txtElement;

        Binding bind = new Binding();
        bind.Path = new PropertyPath("Servo" + Index);
        bind.Converter = textConverter;
        txtElement.SetBinding(TextBlock.TextProperty, bind);

        Binding bind1 = new Binding();
        bind1.Path = new PropertyPath("Servo" + Index);
        bind1.Converter = colorConverter;
        txtElement.SetBinding(TextBlock.BackgroundProperty, bind1);

        return dt;
    }

}

OUTPUT

enter image description here

NOTES

This is a very basic sample which simulate the scenario you mentioned in your question with all mock data. I took every class, converters in single page only. You need to take care of object references / namespaces while managing proper folder structure for your project. I tested this code and this is working as attached image.

Sign up to request clarification or add additional context in comments.

16 Comments

Thanks a lot, I will try to understand the details. and update the status after testing.
@JamesMA Please go through it and also I added "NOTES". Let me know. In nutshell, I moved everything to code behind.
Thanks a lot for the sample, it seems work in my case. I've try to study the code, as I haven't try building listview from code before, I'd like to clarify some logic here. It seems that a data table has been created with dynamic columns containing the value of GetLED and GetServo value separated by "##". Moving to my case, the content of element in lstSubData becomes GetServo(i) + "##" + GetLED(i). And the converters (TextConverter & ColorConverter) will work on those dynamic columns to extract the Text and Color from it.
In this case, it may need to rebuild the DataTable whenever the data is updated. Isn't it? Does it mean that the listview cannot link to a method directly, so it has to create the element with the final value? BTW, may I know how to set the textalignment in code? I can't find such property in FrameworkElementFactory.
Yes. You have to rebuild DataTable each time. I think , there is not other way in your case.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.