Click here to Skip to main content
15,897,518 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Have been trying to develop a composite check box control that essentially replicates how an Excel autofilter dropdown works (just the behavior of the UI, not the filtering part). So it could be thought of as a single level TreeView, where the parent is labeled "(Select All)" and has a tri-state checkbox (unchecked, checked, mixed). The children beneath would have normal checkboxes, either checked or unchecked.

The only other wrinkle is that the children would/could have multiple columns - depending on what data has been loaded (whereas an Excel dropdown autofilter has just one column - the unique values).

The idea was to make it a generic control that could be (re)used anywhere. And, if I could ever get it to work(!), that I'd post here.

I'm fairly new to this type of development, but I've been trying to use a ContainerControl with two ListView controls inside (both with their View property set to "Details"). The first (header) ListView has just one item - with it's text set to "(Select All)". The second (body) ListView would have multiple columns and rows depending on what data is loaded. And they would interact with each other the way an Excel autofilter behaves.

Anyway, assuming anybody is still reading this far(!), my question is am I on the right track given what I'm trying to do, or is there a better approach?
Posted
Comments
Sergey Alexandrovich Kryukov 7-Jun-13 23:03pm    
I would ask you: did you try anything so far?

The problem is: I cannot see how it could require an expert advice, which would not be a whole design + development work. In other words, I think that is just the work to be done, not a matter of sharing any "professional secrets" or little known documentation of open source works...

—SA
Member 10014315 8-Jun-13 12:07pm    
Thanks, Sergey. I've tried a few approaches that didn't seem to be getting anywhere. Using a WinForms ContainerControl seems the most promising, but I'm currently stuck on how to detect events firing from the ListView controls within the ContainerControl. I'll continue to work at it, but the intention of my post was primarily just to get confirmation that my approach is at least feasible. Side benefit might be if somebody knew of a better way to accomplish what I'm trying to do.
TIA
Sergey Alexandrovich Kryukov 10-Jun-13 17:46pm    
I think I know where did you stuck, please see my answer.
—SA
Sergey Alexandrovich Kryukov 10-Jun-13 17:58pm    
By the way, I did not get a notification on your post. Here is why: you should reply to your message (see "Reply" bitmaps above), not to your own post. I occasionally saw your post; if I did not, I would not get a change to post a solution.
—SA
Member 10014315 11-Jun-13 10:04am    
Thanks for your posts, Sergey. I generally follow what your code does - but am not yet proficient enough to understand exactly how(!). I need to read up on anonymous methods and lamda operators, neither of which I'd heard of. I've stored your code to revisit later when I'm sure I'll understand it better.
I think you have a typo near the top where you declare the CheckBox variable. You have "public int" whereas it presumably should be "public Checkbox"(?).
Thanks again for taking the time to reply in such detail.

1 solution

Member 10014315 wrote:
Using a WinForms ContainerControl seems the most promising, but I'm currently stuck on how to detect events firing from the ListView controls within the ContainerControl.
This is really the question, which is specific enough to worth answering; and answering it can really help you.

I think you simply need an idea how to delegate events of some child controls to a parent control the way they can be handled by the users of your controls, without exposing the children themselves (which would be possible but of course would violate proper encapsulation).

The idea is simple enough, just on the example of only one event:
C#
using System;
using System.Collections.Generic;
using System.Windows.Forms;

public class CheckBoxCollectionEventArgs : System.EventArgs {
   internal CheckBoxCollectionEventArgs(int index, CheckBox checkBox) {
       this.checkBoxIndex = index;
       this.checkBox = checkBox;
   } // CheckBoxCollectionEventArgs
   public int checkBoxIndex { get; internal set; }
   public CheckBox checkBox { get; internal set; }
} // class CheckBoxCollectionEventArgs

public class MyCompositeCheckBox /* ... */ {

    List<CheckBox> checkBoxes = new List<CheckBox>(); // for example
    // ...

    public MyCompositeCheckBox() {
        // ...
        for(int index = 0; index < checkBoxes.Count; ++index) { // not foreach,
                                  // because index is used in invocation
             CheckBox checkBox = checkBoxes[index];
             checkBox.CheckStateChanged += (sender, eventArgs) => {
                 if (CheckBoxCollectionCheckedStateChanged != null)
                     CheckBoxCollectionCheckedStateChanged.Invoke(
                         this,
                         new CheckBoxCollectionEventArgs(index, checkBox));
             }; //checkBox.CheckStateChanged
        } // loop
    } // MyCompositeCheckBox

    public System.EventHandler<CheckBoxCollectionEventArgs>
       CheckBoxCollectionCheckedStateChanged;

} // class MyCompositeCheckBox


[EDIT]

As you expressed some concerns about lambda, here is the anonymous method without lambda syntax:
C#
//...
              checkBox.CheckStateChanged += delegate(
                 object sender,
                 CheckBoxCollectionEventArgs eventArgs) {
                 if (CheckBoxCollectionCheckedStateChanged != null)
                     CheckBoxCollectionCheckedStateChanged.Invoke(
                         this,
                         new CheckBoxCollectionEventArgs(index, checkBox));
             }; //checkBox.CheckStateChanged

//...

You can use such syntax even with .NET Framework 2.0 (I don't think any earlier version could be considered seriously, but 2.0 is a decent version, when generics were introduced, as well as anonymous methods and other important stuff).

However, in this very application purpose, you don't have to understand all the power and theory of lambda, which is a whole big fundamental field of software. For this example, it's just enough to understand the syntax formally, as well as the simple case of type inference. So, there is no much of lambda here, I used it mostly to be able to enjoy type inference: as you can see, I did not have to enter the types for sender and eventArgs parameters. Why? They are real types, exactly the same as in lambda-free version of code shown above, only the types are inferred from — where? From the declaration of the event on the left side of '+=', that is, System.EventHandler<CheckBoxCollectionEventArgs>.

Maybe, it's more important to understand closures, which is a very fundamental feature in many language, and the very fundamental phenomenon in modern (and not so modern) languages and systems. This is how you can pass those local (stack) variables to the handler, to allow the user to know the index and the instance of the check box which invoked the original event. Please see:
http://en.wikipedia.org/wiki/Closure_(computer_science)[^].

[END EDIT]

Are you getting the idea? You pre-create some event handlers in your application (shown as one single anonymous method, index is passed using closure) and implement it using the invocation of customer-supplied event handler, not forgetting about checking it for null.

—SA
 
Share this answer
 
v10

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900