2

I have following entity:

public class MyEntity
{
   public int Id {get;set}
   public Color SelectedColors 
}

MyEntity has One-to-Many relation with Color enum.

[Flags]
public enum Color
{
   None=1,
   White=2,
   Red=3,
   Blue=4 
}

In the other word, every myEntity object may have one or more value from Color enum:

myEntity1.Color = Color.Red | Color.White;

I saved this data using Entity Framewrok:

using (var ctx = new MyContext())
{
   var entity1 = new MyEntity { SelectedColors = Color.Blue | Color.White };
   ctx.MyEntities.Add(entity1);
   ctx.SaveChanges();
}

and read it using following code:

using (var ctx = new MyContext())
{
   var entity1 = ctx.MyEntities.Find(id); 
}

I want to show the selected colors by tick on chechboxes,

I used a ListView control(WinForms project) to do this job:

listView1.CheckBoxes = true;
listView1.HeaderStyle = None;
listView1.View = List;

and used following code to show all Enum values as ListView.Items:

foreach (var value in Enum.GetValues(typeof(Color)).Cast<Color>())
{
   listView1.Items.Add(new ListViewItem() 
                           {
                             Name = value.ToString(),
                             Text = value.ToString(),
                             Tag = value
                           });
}

enter image description here

Is there any way to bind SelectedColors values of my query result to listView1.Items?

[Updated]

I saw a solution in this link that Nick-K inherited a new control from ListView. I think that solution isn't good for me, because the inherited control take a DataSource and a DataMember, so what should I set for DataMember in my case(SelectedColors may have more than one value)?

Masoud
  • 8,020
  • 12
  • 62
  • 123
  • Possible duplicate of [Is it possible to bind a List to a ListView in WinForms?](http://stackoverflow.com/questions/2799017/is-it-possible-to-bind-a-list-to-a-listview-in-winforms) – MSL Oct 05 '16 at 07:22
  • In order to use databinding, you need a control and an entity with a property of the same type, in your case `Color`. It seems like you have no other option than inheriting a custom control from `ListView` (or `CheckedListBox`). Then your `DataSource` is the entity and the `DataMember` is `SelectedColors`. Don't worry about the list-thing because `[Flags] enum` is not really a list, even if it can have multiple values. Try to imagine it like an integer where you can set and unset each bit individually by a given name. e.g. None: 0000, White: 0001, White | Blue: 0101 – Kai Thoma Oct 07 '16 at 09:48

3 Answers3

0

ListView class does not support design time binding

Look at this

http://www.codeproject.com/Articles/10008/Data-binding-a-ListView

First of all your enum should be like this:

[Flags]
public enum MyColors
{
   None = 0,
   White = 1,
   Red = 2,
   Blue = 4 
}

Use this Property

public List<string> SelectedColorsList
{
    get
    {
        return SelectedColors.ToString()
               .Split(new[] { ", " }, StringSplitOptions.None)
               .ToList();
    }
    set
    {
        if (value == null)
        {
             this.SelectedColors = MyColors.None;
             return;
        }

        int s = 0;
        foreach (var v in value)
        {
            s += (int)Enum.Parse(typeof(MyColors), v);
        }

        this.SelectedColors = (MyColors)s;
    }
}
MSL
  • 990
  • 1
  • 12
  • 28
  • So, what is `DataMember` in my case? `SelectedColors` may have more than one value. – Masoud Oct 05 '16 at 08:08
  • Create a new property of type list and in get convert your enum to list and in set convert list to enum. then bind this property to datamember – MSL Oct 05 '16 at 08:19
  • 1-How can I convert the `SelectedColors` value to list? could you plz show me with code? 2- I want to check the checkboxes in the ListView for `SelectedColors` values. – Masoud Oct 05 '16 at 08:28
0

If you want to edit your color, you can inherit your own control from ListView for that:

public class ColorListControl : ListView
{
    public event EventHandler SelectedColorChanged;

    private Color selectedColor = Color.None;

    [DefaultValue( Color.None )]
    public Color SelectedColor
    {
        get { return selectedColor; }
        set
        {
            if( selectedColor != value )
            {
                selectedColor = value;

                foreach( ListViewItem item in this.Items )
                {
                    Color itemColor = (Color)item.Tag;
                    if( itemColor == Color.None ) //see http://stackoverflow.com/questions/15436616
                        item.Checked = value == Color.None;
                    else
                        item.Checked = value.HasFlag( itemColor );
                }

                SelectedColorChanged?.Invoke( this, EventArgs.Empty );
            }
        }
    }

    public ColorListControl()
    {
        this.CheckBoxes = true;
        this.HeaderStyle = ColumnHeaderStyle.None;
        this.View = View.List;

        foreach( Color value in Enum.GetValues( typeof( Color ) ).Cast<Color>() )
        {
            this.Items.Add( new ListViewItem()
            {
                Name = value.ToString(),
                Text = value.ToString(),
                Tag = value
            } );
        }
    }

    protected override void OnItemChecked( ItemCheckedEventArgs e )
    {
        base.OnItemChecked( e );

        Color checkedColor = (Color)e.Item.Tag;

        if( e.Item.Checked )
            SelectedColor |= checkedColor;
        else
            SelectedColor &= ~checkedColor;
    }
}

And afterwards you can bind to its property SelectedColor:

public class MainForm : Form
{
    private ColorListControl listView1;

    public MainForm()
    {
        InitializeComponent();

        MyEntity entity = new MyEntity { SelectedColors = Color.Blue | Color.White };

        listView1.DataBindings.Add( nameof( listView1.SelectedColor ), entity, nameof( entity.SelectedColors ) );
    }

    private void InitializeComponent()
    {
        this.listView1 = new ColorListControl();
        this.SuspendLayout();
        // 
        // listView1
        // 
        this.listView1.Location = new System.Drawing.Point(16, 16);
        this.listView1.Name = "listView1";
        this.listView1.Size = new System.Drawing.Size(200, 128);
        this.listView1.TabIndex = 0;
        this.listView1.SelectedColorChanged += new System.EventHandler(this.listView1_SelectedColorChanged);
        // 
        // MainForm
        // 
        this.ClientSize = new System.Drawing.Size(318, 189);
        this.Controls.Add(this.listView1);
        this.Name = "MainForm";
        this.Text = "Form";
        this.ResumeLayout(false);

    }

    private void listView1_SelectedColorChanged( object sender, EventArgs e )
    {
        this.Text = listView1.SelectedColor.ToString();
    }
}

As @MSL already said: you need to define the numbers of your Color enum in powers of two (0, 1, 2, 4, 8, 16...). See https://msdn.microsoft.com/library/system.flagsattribute.aspx

To compile this example you need C# 6.0 / Visual Studio 2015. Otherwise you have to replace nameof( listView1.SelectedColor ) with "SelectedColor".

Instead of using a ListView you can consider using a CheckedListBox as well:

public class ColorListControl : CheckedListBox
{
    public event EventHandler SelectedColorChanged;

    private Color selectedColor = Color.None;

    [DefaultValue( Color.None )]
    public Color SelectedColor
    {
        get { return selectedColor; }
        set
        {
            if( selectedColor != value )
            {
                selectedColor = value;

                for( int i = 0; i < this.Items.Count; i++ )
                {
                    Color itemColor = (Color)this.Items[i];
                    if( itemColor == Color.None )
                        this.SetItemChecked( i, value == Color.None );
                    else
                        this.SetItemChecked( i, value.HasFlag( itemColor ) );
                }

                SelectedColorChanged?.Invoke( this, EventArgs.Empty );
            }
        }
    }

    public ColorListControl()
    {
        CheckOnClick = true;

        foreach( Color value in Enum.GetValues( typeof( Color ) ) )
            this.Items.Add( value );
    }

    protected override void OnItemCheck( ItemCheckEventArgs ice )
    {
        base.OnItemCheck( ice );

        Color checkedColor = (Color)this.Items[ice.Index];

        if( ice.NewValue == CheckState.Checked )
            SelectedColor |= checkedColor;
        else
            SelectedColor &= ~checkedColor;
    }
}
Kai Thoma
  • 512
  • 4
  • 14
  • Thanks, your solution works :). you said I can use `CheckedListBox` too, but I can't set the `SelectionMode` to Multi. Do you have any other solution? – Masoud Oct 08 '16 at 06:45
  • Correct, for CheckedListBox objects, multiple selection is not supported. But this doesn't matter because it only determines how many items can be selected (marked with blue background). But you can check (tick checkboxes) as many items as you want. I've edited my answer to show how to do this with a CheckedListBox. – Kai Thoma Oct 10 '16 at 05:33
  • It works, but when I run program multiple time, the `ColorListControl ` contains multiple set of Colors, In the other word `ColorListControl ` contains `None, Red , Green , Blue , None , Red, Green , Blue,...`. (in the designe-time and run-time, too) do you know where is the problem? – Masoud Oct 17 '16 at 06:43
  • In my above code the control is filled in the constructor. Make sure you did not fill it a second time from somewhere else. Maybe the VS designer created some code into `InitializeComponent()`. If so, remove it from there and move the filling code from constructor to `override OnLoad()`. If all of this does not work then please ask a new question and provide your current code. – Kai Thoma Oct 17 '16 at 06:56
0

If you only want to view the colors, you can just set Checked-property of ListViewItem without any databinding or custom control:

foreach (var value in Enum.GetValues(typeof(Color)).Cast<Color>())
{
    listView1.Items.Add(new ListViewItem() 
                 {
                     Name = value.ToString(),
                     Text = value.ToString(),
                     Checked = entity1.SelectedColors.HasFlag( value ),
                     Tag = value
                  });
}
Kai Thoma
  • 512
  • 4
  • 14