Невозможно привязать данные к элементу управления, когда Control.Visible == false - PullRequest
9 голосов
/ 22 марта 2012

В WinForms с C # 4.0 / C # 2.0 я не могу привязать к элементу управления, если видимое поле элемента управления имеет значение false:

this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done");

Я могу подтвердить, что привязка успешно добавлена ​​в список привязок данных элемента управления, но еслиЯ изменяю свой связанный объект (WorkStatus), ничего не происходит.

Вот как выглядит WorkStatus:

public class WorkStatus : INotifyPropertyChanged
{
    private Boolean _done;
    public Boolean Done
    {
        get { return _done; }

        set
        {
            if (_done == value) return;

            _done = value;

            // fire event
            RaisePropertyChanged("Done");
        }
    }

    private Int32 _time;
    public Int32 Time
    {
        get { return _time; }

        set
        {
            if (_time == value) return;

            _time = value;

            // fire event
            RaisePropertyChanged("Time");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(String propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null) { PropertyChanged(this, e); }
    }
}

Редактировать
Для воспроизведения просто установите Visible = false в конструкторе или в конструкторе передпривязка данных.
Использование одной перегрузки метода Add () тоже не удается:

this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done",
   true, DataSourceUpdateMode.OnPropertyChanged);

Причина, по которой я хочу скрыть элемент управления, заключается в том, что я не хочу, чтобы пользователь видел элемент управления при формепоказано в самый первый раз.

Решение
Спасибо, ребята, я думаю, что нашел решение для этого:

просто установите Control.Visible = false в событии Form.Load (),В этом случае элемент управления не отображается при отображении формы.

Хотя, почему MS проектирует привязку данных таким способом, пока неизвестно.

Ответы [ 7 ]

11 голосов
/ 22 марта 2012

Я столкнулся с этой точной ситуацией до .Пока элемент управления не станет жизнеспособным в первый раз, некоторая внутренняя инициализация никогда не произойдет, часть этой инициализации включает привязку данных.Вы должны позвонить CreateControl(true) до того, как сработает привязка данных.Тем не менее, этот метод является защищенным, поэтому вы должны сделать это путем отражения или расширения элемента управления.

через отражение:

private static void CreateControl( Control control )
{
    var method = control.GetType().GetMethod( "CreateControl", BindingFlags.Instance | BindingFlags.NonPublic );
    var parameters = method.GetParameters();
    Debug.Assert( parameters.Length == 1, "Looking only for the method with a single parameter" );
    Debug.Assert( parameters[0].ParameterType == typeof ( bool ), "Single parameter is not of type boolean" );

    method.Invoke( control, new object[] { true } );
}

Все события будут отложены до тех пор, пока для элемента управления Created не будет установлено значение true.

1 голос
/ 04 января 2015

Я знаю, что это немного поздно, но у меня возникла та же проблема - элемент управления, к которому я хочу привязаться, установлен на visible = false, когда отображается форма. Возможно, я хочу сделать это во многих формах, и я всегда неохотно пишу код для каждой привязки.

Итак, я собрал небольшой хак.

У меня есть форма с панелью, в которой я установил Visible = false в конструкторе. Я хочу связать представление с пользовательской моделью представления, которую я написал. В форме я добавляю BindingSource из панели инструментов. I DataSource источника привязки к источнику данных проекта для моей модели представления.

В таком случае идея состоит в том, чтобы перебирать элементы управления в форме и обновлять связанное значение элементов управления из источника данных (который является моделью представления).

Для этого я сохраняю видимое значение элемента управления, устанавливаю его в значение false и считываю значение привязки. Затем восстановите исходное видимое значение. Это делается с помощью метко названного метода HackIt ().

Вот код:

ViewModel

public class SimpleViewModel // : DomainModelBase - add your notify property changed
{
    public SimpleViewModel()
    {
        _visible = true;
    }


    private bool _visible;
    public bool Visible
    {
        get
        {
            return _visible;
        }
        set
        {
           _visible = value;
           OnPropertyChanged("Visible");
        }
    }
}

Код конструктора форм

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.components = new System.ComponentModel.Container();
        this.panel1 = new System.Windows.Forms.Panel();
        this.bindingSource1 = new System.Windows.Forms.BindingSource(this.components);
        this.button1 = new System.Windows.Forms.Button();
        this.button2 = new System.Windows.Forms.Button();
        ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).BeginInit();
        this.SuspendLayout();
        // 
        // panel1
        // 
        this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0)))));
        this.panel1.DataBindings.Add(new System.Windows.Forms.Binding("Visible", this.bindingSource1, "Visible", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
        this.panel1.Location = new System.Drawing.Point(94, 85);
        this.panel1.Name = "panel1";
        this.panel1.Size = new System.Drawing.Size(200, 100);
        this.panel1.TabIndex = 0;
        // 
        // bindingSource1
        // 
        this.bindingSource1.DataSource = typeof(WindowsFormsBindVisible.SimpleViewModel);
        // 
        // button1
        // 
        this.button1.Location = new System.Drawing.Point(74, 34);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(75, 23);
        this.button1.TabIndex = 1;
        this.button1.Text = "button1";
        this.button1.UseVisualStyleBackColor = true;
        // 
        // button2
        // 
        this.button2.Location = new System.Drawing.Point(155, 34);
        this.button2.Name = "button2";
        this.button2.Size = new System.Drawing.Size(75, 23);
        this.button2.TabIndex = 2;
        this.button2.Text = "button2";
        this.button2.UseVisualStyleBackColor = true;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(500, 261);
        this.Controls.Add(this.button2);
        this.Controls.Add(this.button1);
        this.Controls.Add(this.panel1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).EndInit();
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.Panel panel1;
    private System.Windows.Forms.BindingSource bindingSource1;
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.Button button2;
}

Код формы

public partial class Form1 : Form
{
    public SimpleViewModel ViewModel = new SimpleViewModel();

    public Form1()
    {
        InitializeComponent();
        this.panel1.Visible = false;

        this.bindingSource1.DataSource = this.ViewModel;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        HackIt();
    }

    void HackIt()
    {
        this.SuspendLayout();
        foreach(Control control in this.Controls)
        {
            var v = control.Visible;
            control.Visible = true;

            foreach(Binding db in control.DataBindings)
            {
                db.ReadValue();
            }

            control.Visible = v;
        }
        this.ResumeLayout();
    }
}

С приведенным выше кодом форма запускается и показывает мой контроль. Вы можете изменить конструктор модели представления и по умолчанию установить false, чтобы скрыть. Это работает в любом случае.

В конструкторе формы - я хочу явно скрыть панель (this.panel1.Visible = false) - просто чтобы подтвердить привязку, когда модель представления по умолчанию visible = true, элемент управления корректно отображается при загрузке.

Затем мы можем заставить кнопки изменять видимое на модели представления, что переключит состояние видимой панели:

    private void button1_Click(object sender, EventArgs e)
    {
        this.ViewModel.Visible = false;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        this.ViewModel.Visible = true;
    }

UPDATE

Это заставило меня преодолеть первое препятствие. Однако я использую компоненты Telerik, поэтому я решил отказаться от элемента управления Telerik на форме. Это полностью сломало все.

Вместо метода HackIt, указанного выше, вызовите следующую функцию RefreshDataBindings () в событии загрузки.

Я решил перебрать все элементы управления в форме и вручную обновить привязку отражающим способом. Это безумие! Но это работает на 100% - даже с контролем Telerik на моей форме. И производительность в моем основном приложении нормальная. Это просто грязный хак - но я поместил его один раз в базовую форму или базовый контроль - и я не беспокоюсь о своих привязках.

protected void RefreshDataBindings()
{
    foreach (Control control in this.Controls)
        RefreshControlBindingsRecursive(control);
}

private void RefreshControlBindingsRecursive(Control control)
{
    if (!control.Visible || !control.Created)
    {
        foreach (Binding db in control.DataBindings)
        {
            if (db.PropertyName == "Visible")
            {
                try
                {
                    object dataSource = db.DataSource is BindingSource ?
                        (db.DataSource as BindingSource).DataSource : db.DataSource;

                    PropertyInfo pi =
                            dataSource.GetType().GetProperty(db.BindingMemberInfo.BindingMember); ;


                    PropertyInfo piC = db.Control.GetType().GetProperty(db.PropertyName);
                    piC.SetValue(db.Control, pi.GetValue(dataSource));
                }
                catch (Exception ex)
                {
                    string s = ""; // not bothered its too late at night
                }
            }
        }
    }

    foreach (Control child in control.Controls)
        RefreshControlBindingsRecursive(child);
}
1 голос
/ 11 апреля 2012

Что вы можете сделать, это сделать элемент управления видимым и снова сделать его невидимым после изменения привязки.

this.checkBox_WorkDone.Visible = true; 
this.checkBox_WorkDone.BindingContextChanged += (object sender, EventArgs e) => {
    this.checkBox_WorkDone.Visible = false; 
};

Не очень красиво, но работает.

1 голос
/ 22 марта 2012

Я создал тестовый жгут (см. Ниже) и попробовал ваш код. Мне нужно было использовать перегрузку метода Add для установки DataSourceUpdateMode.OnPropertyChanged.

public partial class Form1 : Form
{
    private readonly WorkStatus _status = new WorkStatus();
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        var t = new Timer();
        t.Interval = 1000;
        t.Tick += (s, ea) => { _status.Done = true; t.Enabled = false; };
        t.Enabled = true;

        checkBox_WorkDone.DataBindings.Add("Visible", _status, "Done", true, DataSourceUpdateMode.OnPropertyChanged);
        base.OnLoad(e);
    }
}

EDIT: Если вы удалите сеттер из конструктора формы, это будет работать нормально. Если вы установили видимость в false в конструкторе формы, эта привязка не сможет обновиться. Нет никакой причины вручную указывать начальную видимость, если ваша привязка данных работает правильно. Это действительно побеждает цель привязки данных в первую очередь.

1 голос
/ 22 марта 2012

ОБНОВЛЕННЫЙ КОД

Это работает для меня с кодом, приведенным в вашем вопросе.

    private WorkStatus m_WorkStatus = new WorkStatus();
    public Form1()
    {
        InitializeComponent();

        this.checkBox_WorkDone.Visible = true;
        this.checkBox_WorkDone.DataBindings.Add("Visible", m_WorkStatus, "Done");
    }

    private void btnToggle_Click(object sender, EventArgs e)
    {
        m_WorkStatus.Done = !m_WorkStatus.Done;
    }

Вы можете установить Control на visible = true перед привязкой.

Если элемент управления невидим, и мы выполняем следующий код, он тоже будет работать:

        this.checkBox_WorkDone.DataBindings.Add("Visible", m_WorkStatus, "Done");
        // Binding does not work till Visible is set to true once.
        this.checkBox_WorkDone.Visible = true;

DataSourceUpdateMode.OnPropertyChanged не требуется! Когда объект WorkStatus имеет Done = false, он не будет отображать элемент управления, а вызовет событие VisibleChanged.

1 голос
/ 22 марта 2012

Попытка использовать эту Add перегрузку:

this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done",
   true, DataSourceUpdateMode.OnPropertyChanged);
0 голосов
/ 05 сентября 2018

Мой обходной путь: private void Form_Load(object sender, EventArgs e) { button.Visible = true; button.DataBindings["Visible"].ReadValue(); }

button.Visible = true; необходим для принудительного создания элемента управления (другими словами, это дескриптор создания окна).

Поскольку привязка данных не будет работать, покаконтроль создан.Поэтому сначала создайте элемент управления.

А затем перезагрузите фактическое значение Visible из источника данных, вызвав Binding.ReadValue().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...