2012-03-22 4 views
9

В WinForms с C# 4.0/C# 2.0, я не могу связать с элементом управления, если видимое поле элемента управления является ложным:Не удается привязать данные к элементу управления, когда Control.Visible == ложь

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 = ложь в конструкторе, или в конструкторе до DataBinding.
Используя одну перегрузку метода Add() не удается слишком:

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

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

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

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

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

+0

Смотрите здесь для ответа, почему DataBinding не работает, если видимость установлена ​​в ложь: http://stackoverflow.com/a/2570153/2455604 – Marwie

ответ

9

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

Via отражение:

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 значение ИСТИНА.

+0

Не знаю, если это необходимо. Я могу воспроизвести его сбой, а затем исправить его, указав «DataSourceUpdateMode» как «OnPropertyChanged». –

+2

'DataSourceUpdateMode.OnPropertyChanged' не работает для меня, если элемент управления ist initialy установлен в Visible = false в конструкторе. – Tarion

+0

.. или просто используйте привязку данных правильно. ' –

1

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

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

+1 - Это работает правильно. –

+2

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

+0

@AZ. - В соответствии с этой страницей (http://msdn.microsoft.com/en-us/library/83fhsxwc.aspx) использование неинициализированных переменных недопустимо. Возможно, это имеет какое-то отношение к '_done', не имеющему значения по умолчанию. Попробуйте установить его ('private Boolean _done = false') и посмотрите, работает ли это – SwDevMan81

1

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

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

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; 
    } 

Вы можете установить элемент управления в 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.

+0

Я сделал то же самое, постараюсь установить Visible в false в конструкторе или установить this.checkBox_WorkDone.Visible = false в конструкторе перед привязкой, вы все равно получите правильный результат? –

+0

@ Tarion - Это не для меня тоже. –

+1

Когда я устанавливаю элемент управления на visible = false перед привязкой, он будет терпеть неудачу - я рассмотрю его более подробно. – Tarion

1

Я создал испытательный жгут (см. Ниже) и попробовал свой код. Мне нужно было использовать перегрузку метода 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 в конструкторе формы, это связывание не будет обновляться. Нет никакой причины вручную указывать начальную видимость, если ваша привязка данных работает правильно. Это в первую очередь поражает цель привязки данных.

+0

См. Мой ответ в ответе SwDevMan81. –

+0

@RitchMelton: рассмотрите случай, когда флажок установлен на стр. 2 tabcontrol. В этом случае поведение такое же, как описано OP ** без ** видимости, заданной явно. В этом случае вы должны прибегнуть к решению, например, предложенному Скоттом. – Andreas

0

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

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

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

1

Я знаю, что это немного поздно в тот же день, но у меня была та же проблема - элемент управления, который я хочу связать, установлен на 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"); 
     } 
    } 
} 

Form Designer Код

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) - просто чтобы доказать привязку, когда модель представления по умолчанию видима = 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); 
} 
Смежные вопросы