11

This is a .net problem with winforms, not asp.net.

I have a windows form with several tabs. I set data bindings of all controls when the form is loaded. But I have noticed that the data bindings of controls on the second tab do not work. Those bindings work only when the form is loaded and when I select the second tab. This brings the suspicion to me: data bindings work only when bound controls become visible.

Anyone can tell me whether this is true or not? It is not hard to test this but I would like to know some confirmation.

Thanks

tdoan
  • 63
  • 8
Steve
  • 4,935
  • 11
  • 56
  • 83

7 Answers7

15

You are correct. A data-bound control are not updated until the control is made visible.

The only reference I can find for this at the moment is this MSDN thread.

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
5

Your issue has to do with the behavior of the TabControl. See Microsoft bug report. I posted a workaround for that problem which subclasses the TabControl and 'Iniatalizes' all the tab pages when the control is created or the handle is created. Below is the code for the workaround.

public partial class TabControl : System.Windows.Forms.TabControl
{
    protected override void OnHandleCreated(EventArgs e_)
    {
        base.OnHandleCreated(e_);
        foreach (System.Windows.Forms.TabPage tabPage in TabPages)
        {
            InitializeTabPage(tabPage, true, Created);
        }
    }

    protected override void OnControlAdded(ControlEventArgs e_)
    {
        base.OnControlAdded(e_);
        System.Windows.Forms.TabPage page = e_.Control as System.Windows.Forms.TabPage;
        if ((page != null) && (page.Parent == this) && (IsHandleCreated || Created))
        {
            InitializeTabPage(page, IsHandleCreated, Created);
        }
    }

    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        foreach (System.Windows.Forms.TabPage tabPage in TabPages)
        {
            InitializeTabPage(tabPage, IsHandleCreated, true);
        }
    }

    //PRB: Exception thrown during Windows Forms data binding if bound control is on a tab page with uncreated handle
    //FIX: Make sure all tab pages are created when the tabcontrol is created.
    //https://connect.microsoft.com/VisualStudio/feedback/details/351177
    private void InitializeTabPage(System.Windows.Forms.TabPage page_, bool createHandle_, bool createControl_)
    {
        if (!createControl_ && !createHandle_)
        {
            return;
        }
        if (createHandle_ && !page_.IsHandleCreated)
        {
            IntPtr handle = page_.Handle;
        }
        if (!page_.Created && createControl_)
        {
            return;
        }
        bool visible = page_.Visible;
        if (!visible)
        {
            page_.Visible = true;
        }
        page_.CreateControl();
        if (!visible)
        {
            page_.Visible = false;
        }
    }
} 
Igby Largeman
  • 16,495
  • 3
  • 60
  • 86
Hasani Blackwell
  • 2,026
  • 1
  • 13
  • 10
  • What exactly is "IntPtr handle = page_.Handle" doing there, considering that the value isn't used? – Michael Jan 22 '14 at 09:39
  • It forces the handle to be created – Hasani Blackwell Jan 23 '14 at 01:19
  • Thanks. I figured it was something like that. I tried this out, btw, but it didn't work for me. My controls were getting added while IsHandleCreated and Created were still false. I imagine some tweaking would cover that case, but I took a different route: After doing all the bindings I find all the bound controls and add a VisibleChanged handler to the invisible ones, which will do what I need when they become visible. – Michael Jan 23 '14 at 08:12
2

We've encountered a similar problem. We're trying to write to 2 bound, invisible fields so that we can change the format that we write to our dataset. This works fine when the objects are visible, but stops working when the visible property was changed to false.

To get round it, I added the following code:

            // Stop our screen flickering.
        chSplitContainer.Panel2.SuspendLayout();
        // Make the bound fields visible or the binding doesn't work.
        tbxValueCr.Visible = true;
        tbxValueDb.Visible = true;

        // Update the fields here.
        <DO STUFF>

        // Restore settings to how they were, so you don't know we're here.
        tbxValueCr.Visible = false;
        tbxValueDb.Visible = false;
        chSplitContainer.Panel2.ResumeLayout();
Colin
  • 1,141
  • 1
  • 9
  • 9
1

I've struggled with this myself and concluded that the only workaround, besides subclassing apparently (see hjb417's answer), was to make the other tab visible. Switching to the other tab and going back to the previous immediately before the form is visible doesn't work. If you do not want to have the second tab visible, I've used the following code as a workaround:

this.tabControl.SelectedTab = this.tabPageB; 
this.tabPageB.BindingContextChanged += (object sender, EventArgs e) => {
    this.tabContainerMain.SelectedTab = this.tabPageA; 
};

Assuming tabPageA is the visible tab, and tabPageB is the invisible one you want to initialize. This switches to pageB, and switches back once the data binding is complete. This is invisible to the user in the Form.

Still an ugly hack, but at least this works. Off course, he code gets even uglier when you have multiple tabs.

Bas
  • 3,016
  • 3
  • 22
  • 18
1

Sorry for necromancing this thread, but it is easy to force the invisible controls' databinding/handles to be ready using this method:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/190296c5-c3b1-4d67-a4a7-ad3cdc55da06/problem-with-binding-and-tabcontrol?forum=winforms

Simply, let's say if your controls are in tab page tpg_Second (or tabCtl.TabPages[1]), before you do anything with their data, call this first:

tpg_Second.Show()

This will not activate any of the tab pages, but viola, the databinding of the controls should work now.

im_chc
  • 1,023
  • 15
  • 24
0

Based on the answers, I made this method that works for me:

 public partial class Form1: Form
 {
    private void Form1_Load(object sender, EventArgs e)
    {
        ...
        forceBindTabs(tabControl1);
    }

    private void forceBindTabs(TabControl ctl)
    {
        ctl.SuspendLayout();

        foreach (TabPage tab in ctl.TabPages)
            tab.Visible = true;

        ctl.ResumeLayout();
    }
 }

In addition to solving the problem, the tabs are loaded at the beginning and are displayed faster when the user clicks on them.

fsbflavio
  • 664
  • 10
  • 22
0

This is not something I've come across directly. However, you might be experiencing a problem with the BindingContext. Without more details it's hard to say, but if I were you I'd set a breakpoint and make sure the controls are all bound in the same context.

overslacked
  • 4,127
  • 24
  • 28