Change labelText in user control outside its own .cs - c#

I have been googling for the last few days but without a result.
I have a Form called MainForm, i placed four user controls on it.
These user controls contain labels and buttons. Now i created a file called Language.cs
In this file i want to change the languages for all labels when a button is pressed in one of my user controls.
When i coded this in ucSettings.cs i would do it like:
this.label1.Text = res_man.GetString("label_text", cul);
But this doesnt work, beccause my Resourcemanager and my Culture info are both in another file.
So i have
MainForm contains four user controls
The user controls are called, ucAnimalInfo, ucAnimalInput, ucSettings and ucMenuStrip
I have a button in ucSettings that would have to change the language
The text should be set in the file Language.cs
As you can see i change the culture info when a language is selected in a ComboBox:
private void Settings_Language_Cbox_SelectedIndexChanged(object sender, EventArgs e)
{
string SelectedIndex = Settings_Language_Cbox.SelectedItem.ToString();
switch (SelectedIndex)
{
case "English(English)":
ci = CultureInfo.CreateSpecificCulture("en-US");
LanguageSelection = 5;
break;
case "Nederlands(Dutch)":
ci = CultureInfo.CreateSpecificCulture("nl-NL");
LanguageSelection = 6;
break;
}
Now i would like to do something like:
private void Settings_Save_Btn_Click(object sender, EventArgs e)
{
Language.Change();
}
So that it calls my Change method in Language.cs and change a the labels to the correct language. But now i am not able to acces any of the labels in Language.cs even tho they are on public, also my Language.cs file isnt able to get the Resourcemanager and CultureInfo from ucSettings.cs. So my question is, wat is the best way to handle this? I tried using a get/set method but this didn't work out at all, now i am not sure if it is because i messed this up or not.
Edit: I got close by doing it like:
ucSettings.cs
private void Settings_Save_Btn_Click(object sender, EventArgs e)
{
Settings_Language_Cbox.SelectedIndex = LanguageSelection;
BusinessClasses.Language language = new BusinessClasses.Language();
language.setLanguage();
}
Language.cs:
public class Language
{
public MainForm mainform;
public ucAnimalInfo animalinfo;
public ucAnimalInput animalinput;
public ucSettings settings;
public void setLanguage()
{
mainform.Animal_Info_Tab.Info_Id_Text.Text = mainform.Settings_Tab.rs.GetString("Info_Save_Btn", mainform.Settings_Tab.ci);
}
it still gives a NullReferenceException so it not really working, but the closest i got so far. rs and ci are the resourcemanager and cultureinfo

Your question is not very clear, you say user controllers but mean? user control...
As you are new to Windows Mobile/Compact Framework you should start with a simple project first and then evolute to what you want, using small steps.
A good starting for language resource localization maybe at http://www.codeproject.com/Articles/28234/Survival-guide-to-do-resource-localization-using-C.
Use that and then extend with a simple user control and so on.
Then also read http://www.codeproject.com/Articles/16091/User-Interface-Localization-with-the-Compact-Frame about changing the languge 'on-the-fly'.
Please just supply your idea / need (I want to allow this and show that) if you need further assistance.

Related

DateTime array element does not contain the correct value

I've created a simple DateTime array that contains 3 items. These items are set to use the values of three different DateTimePickers on my form. Before I go further into using the array, I need to make sure it is actually using the correct values, and it does not appear to be doing so. Here's my code:
namespace Test
{
public partial class Form1 : Form
{
DateTime[] monSchedule = new DateTime[3];
public Form1()
{
InitializeComponent();
monSchedule[0] = monStart.Value;
monSchedule[1] = monEnd.Value;
monSchedule[2] = monLunch.Value;
}
private void Form1_Load(object sender, EventArgs e)
{
setDefaults();
}
private void setDefaults()
{
monStart.Value = DateTime.Parse("00:00");
monEnd.Value = DateTime.Parse("00:00");
monLunch.Value = DateTime.Parse("00:00");
}
private void validate()
{
MessageBox.Show("You entered time " + monSchedule[0]);
}
When I load my form, setDefaults(); should change the values to the current date with a time of 00:00. When I press the button to show the value in the array, it is pulling current date and current time. I need it to pull whatever the current time in that DateTimePicker is. So if a user types 10:00 into the DateTimePicker (they are formatted HH:mm), then I need the MessageBox to say the time is 10:00 AM. If I change the value to 22:00, then I need the messagebox to say the time is 10:00 PM. etc. (Date is irrelevant in my scenario, I'm not concerned with what the date is at all. Only the time.)
I suspect it may be because of the order it's written in. Is the array storing the value of the DateTimePicker BEFORE setDefaults(); is run? If so, how do I make the values of the array items dynamic since the values of the DateTimePickers are going to change a lot and I need the array elements to be updating with the latest values?
EXTRA INFO:
-Using Visual Studio
-Added the DateTimePickers in design view, changed the format to HH:mm there, did not change the default values in design view
-Ignoring date completely, only concerned with time right now
PS: I was also struggling with where to declare the array so it was accessible in multiple other methods and found I had to declare the array initializer within public partial class Form1, but then add the items in the array within public Form1(), because it wouldn't let me add them under public partial class Form1. I don't know if this is correct though, but it seemed to work when I tested with an array of strings so I went with it.
I have to say that this is a bit of a regression. In your previous question, JoshPart gave you good advice in the form of user controls, although he may have left some gaps too large for you to fill on your own.
Using arrays in this manner might work for a single day, but it won't scale well to a full week.
In case anyone reading this is wondering why I'm talking about a full week, I refer you to the previous question. Also, I recognize that I'm going off-topic for this specific question but I believe this to be an XY problem and the previous question was actually based on the real problem and work that was more on-the-mark.
Let's start with what we know. I've gleaned this from the two questions and the various comments in both.
You have DateTimePicker controls for start, end, and lunch. You're only interested in the time portion so you have Format set to "Custom" and CustomFormat set to "HH:mm". Assumption: lunch is a fixed length so the end time isn't needed.
You have the aforementioned controls times seven, one set for each day of the week.
You've written validation code (range tests) to determine if values are entered correctly, and you're able to show a label with red exclamation marks when that test fails.
You've identified that it's getting too complicated just having a bunch of controls on a form.
So far, so good. Now for your goal.
You're looking for a way to organize the controls, and the data they collect, to make it easier to work with them.
A user control is still the way to go here. You'll benefit from encapsulating all that repeated functionality into a single place and being able to reuse it.
Start by creating a user control -- we'll call it DayPanel -- and put all the controls for a single day on that canvas. Name the controls without any regard for the day of week (e.g. start, lunch, and end). Your user control will neither know nor care which day it represents.
Add an event handler for the ValueChanged event to the DateTimePicker controls. Instead of double-clicking the control, go to the events list in the Properties tool window and type a name, such as the one below, for the ValueChanged event. Do the same for the other two controls and it will reuse the event handler that it created the first time. Whenever the user changes a time, this event handler will be called and it will effect changes to the UI.
private void picker_ValueChanged(object sender, EventArgs e)
{
// In case you need to know which DateTimePicker was changed, take a look at 'sender':
//DateTimePicker picker = (DateTimePicker)sender;
UpdateWarningState();
}
As Jimi mentioned, the sender object will be a reference to the DateTimePicker control that sent the event. You probably won't need it but it's there if you do.
UpdateWarningState just hides/shows the warning label based on the validity of the inputs.
private void UpdateWarningState()
{
warningLabel.Visible = !IsInputValid(start.Value.TimeOfDay, lunch.Value.TimeOfDay, end.Value.TimeOfDay);
}
I had suggested in comments on the previous question that it seemed to make sense to get true if the inputs are valid and then use the logical negative for the visibility of the warning label.
As Paul Hebert pointed out, you really only need to compare a TimeSpan, so IsInputValid receives the TimeOfDay property to deal with only that much.
private bool IsInputValid(TimeSpan startTime, TimeSpan lunchTime, TimeSpan endTime)
{
return startTime < lunchTime && lunchTime.Add(TimeSpan.FromMinutes(30)) < endTime;
}
In fact, even though you're only inputting a time, the control still returns a date part in its Value property. If you want to be certain that you're not comparing times in different dates, you'll definitely need to use the TimeOfDay property. That said, by not presenting the date part, you have a measure of control over that so it's not a pressing concern. If you had to worry about crossing over midnight, that would complicate things.
Note that I've dealt with that earlier assumption that lunch is a fixed length by adding 30 minutes in the comparison to the end time.
Why not just do all that in the ValueChanged event handler?
The Single Responsibility Principle. IsInputValid does one thing: business logic; it tells you if the inputs are valid based on range testing. UpdateWarningState does a different thing: UI logic; it updates the visibility of warning label based on the validity of the inputs.
UpdateWarningState is reusable. You can call it from other event handlers in the future. Event handlers really shouldn't ever do much. They're more like telephone operators: "how may I direct your call?"
IsInputValid is reusable. The business logic can be extracted from your UI code at some point in the future and be reused by something else. I'll admit that the name leaves something to be desired; it fits here but probably should be different outside this context.
But what good is this user control if you have no way of working with its data? The consumer needs to be able to interact with it. A user control is just another class so you can define public properties, methods, and events as you see fit. We'll add properties for the three values of interest:
public TimeSpan Start
{
get => start.Value.TimeOfDay;
set => start.Value = start.Value.Date + value;
}
public TimeSpan Lunch
{
get => lunch.Value.TimeOfDay;
set => lunch.Value = lunch.Value.Date + value;
}
public TimeSpan End
{
get => end.Value.TimeOfDay;
set => end.Value = end.Value.Date + value;
}
What's interesting to note about these properties is that they don't have their own backing storage. Instead, they defer to the controls and translate between their own TimeSpan data type and the controls' DateTime data type. On get, they return just the TimeOfDay property. On set, they remove the time portion (with .Date) and add the time of day.
If you were building this for someone else to consume, you'd want to ensure that the Days property is 0 and that the whole value is non-negative, and either throw ArgumentOutOfRangeException or (gasp!) clamp the value to the acceptable range.
Now that you have a functioning control for a single day, you can slap a bunch of them on the main form. Back in Form1, add seven instances of the DayPanel control and name them monday through sunday. Before we get to initialization, let's create a lookup for these user controls.
private readonly Dictionary<DayOfWeek, DayPanel> _dayPanelLookup;
public Form1()
{
InitializeComponent();
_dayPanelLookup = new Dictionary<DayOfWeek, DayPanel>()
{
[DayOfWeek.Monday] = monday,
[DayOfWeek.Tuesday] = tuesday,
[DayOfWeek.Wednesday] = wednesday,
[DayOfWeek.Thursday] = thursday,
[DayOfWeek.Friday] = friday,
[DayOfWeek.Saturday] = saturday,
[DayOfWeek.Sunday] = sunday
};
}
Now the Load handler can then initialize all the properties. This DefaultTime duplicates the TimeSpan.Zero constant for the purpose of giving it a distinct meaning and can help with refactoring later on.
private static readonly TimeSpan DefaultTime = TimeSpan.Zero;
private void Form1_Load(object sender, EventArgs e)
{
SetDefaults();
}
private void SetDefaults()
{
foreach (DayPanel dayPanel in _dayPanelLookup.Values)
{
dayPanel.Start = DefaultTime;
dayPanel.Lunch = DefaultTime;
dayPanel.End = DefaultTime;
}
}
And just for fun, we can use _dayPanelLookup to grab one of them based on a variable containing the day of the week.
public void someButton_Click(object sender,
{
DayOfWeek whichDay = SelectADay();
DayPanel dayPanel = _dayPanelLookup[whichDay];
// ...
}
That should address the main concern of organizing the controls and making it easy to work with them and their values. What you do with it once the user presses some as-yet-unidentified button on the form is a whole new adventure.
There are yet better ways of doing all of this, I'm sure. I'm not a UI developer, I just play one on TV. For your purposes, I hope this not only gives you the guidance you needed at this point in this project but also illuminates new avenues of thought about how to structure your programs in the future.
It is unclear what you want the date component of the date part of DateTime to be DateTime.Parse("00:00") should return midnight today or 12/27/18 12:00:00 AM;
This is also the same value as DateTime.Today
In addition, you can create a new DateTime with a constructor
monStart.Value = new DateTime(2018, 12, 27, 0, 0, 0);
This is midnight the today
Note:
Reading the description in your updated question, it appears that the DateTimePicker controls values are accessed on a Button Click. If this is the actual scenario, you probably don't need a DateTime array field at all: you could just read the values directly from the DTP controls and use the values in-place.
The example assumes (to comply with the question) that you need that array anyway.
A possible way to proceed:
Set the default values in the Form.Load event. Initialize the monSchedule array values right after, so the values are synchronized. Note that the Form.Load event handler code is (of course) executed after the class constructor (public Form1() { }): the Form object must be already initialized.
Assign an event handler to all the DateTimePicker controls (same event for all). The event handler is used to assign the new values to the monSchedule array. The event could be the ValueChanged event or, possibly, the more generic Validating event. The former is raised each time you change any part of the Time value (the hour value or minutes value). The latter only when the control loses the focus. Your choice.
Use the sender object in the event handler to determine which control raised the event and update the corresponding array value.
An example, using a switch statement and a case statement with a when clause:
Notes:
1. You need C# 7.0+ to use this switch syntax. Otherwise, you could switch using a Type pattern (see the Docs) or the DateTimePicker name (see the example).
2. The DTP_ValueChanged(object sender, EventArgs e) event (the ValueChanged handler) is assigned to all the DateTimePicker controls.
public partial class Form1 : Form
{
DateTime[] monSchedule = new DateTime[3];
private void Form1_Load(object sender, EventArgs e)
{
SetDefaultDTPValues();
}
private void SetDefaultDTPValues()
{
monStart.Value = DateTime.Parse("00:00");
monEnd.Value = DateTime.Parse("00:00");
monLunch.Value = DateTime.Parse("00:00");
monSchedule[0] = monStart.Value;
monSchedule[1] = monEnd.Value;
monSchedule[2] = monLunch.Value;
}
private void DTP_ValueChanged(object sender, EventArgs e)
{
switch (sender)
{
case DateTimePicker dtp when dtp.Equals(monStart):
monSchedule[0] = dtp.Value;
break;
case DateTimePicker dtp when dtp.Equals(monEnd):
monSchedule[1] = dtp.Value;
break;
case DateTimePicker dtp when dtp.Equals(monLunch):
monSchedule[2] = dtp.Value;
break;
}
}
}
On a Button.Click event:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show($"Start: {monSchedule[0].ToString("hh:mm tt")} " +
$"End: {monSchedule[1].ToString("hh:mm tt")} " +
$"Lunch: {monSchedule[2].ToString("hh:mm tt")}");
}
If the C# version in use doesn't allow this switch statement syntax, you can use the DateTimePicker name instead (there are other options, see the examples in the Docs):
private void DTP_ValueChanged(object sender, EventArgs e)
{
DateTimePicker dtp = sender as DateTimePicker;
switch (dtp.Name)
{
case "monStart":
monSchedule[0] = dtp.Value;
break;
case "monEnd":
monSchedule[1] = dtp.Value;
break;
case "monLunch":
monSchedule[2] = dtp.Value;
break;
}
}

Change App language at RunTime on-the-fly

I'm currently developing a metro app in which the user can change current language at runtime and all the custom controls that are loaded must update their text regarding to the new language. Problem is that when I change the language using the following code, the app language changes but it will only update text when I restart my app because the pages and controls that are already rendered are cached.
LocalizationManager.UICulture = new System.Globalization.CultureInfo((string)((ComboBoxItem)e.AddedItems[0]).Tag);
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = ((ComboBoxItem)e.AddedItems[0]).Tag as String;
What should I do to force updating text of all custom controls at runtime without restarting my app?
Use this:
var NewLanguage = (string)((ComboBoxItem)e.AddedItems[0]).Tag;
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = NewLanguage;
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
//Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceManager.Current.DefaultContext.Reset();
and then reload your Page, using Navigate method:
if (Frame != null)
Frame.Navigate(typeof(MyPage));
In order to respond right away, you would need to reset the context of the resource manager.
For Windows 8.1:
var resourceContext = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
resourceContext.Reset();
You will still need to force your page to redraw itself and thus re-request the resources to get the changes to take place. For Windows 8, you can see https://timheuer.com/blog/archive/2013/03/26/howto-refresh-languages-winrt-xaml-windows-store.aspx
You can change the app's language at runtime with the help of this source code. I took help from this and manipulated my app's language settings page as follows:
In languageSettings.xaml.cs:
public partial class LanguageSettings : PhoneApplicationPage
{
public LanguageSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (ChangeLanguageCombo.Items.Count == 0)
{ ChangeLanguageCombo.Items.Add(LocalizationManager.SupportedLanguages.En);
ChangeLanguageCombo.Items.Add(LocalizationManager.SupportedLanguages.Bn);
}
SelectChoice();
}
private void ButtonSaveLang_OnClick(object sender, RoutedEventArgs e)
{
//Store the Messagebox result in result variable
MessageBoxResult result = MessageBox.Show("App language will be changed. Do you want to continue?", "Apply Changes", MessageBoxButton.OKCancel);
//check if user clicked on ok
if (result == MessageBoxResult.OK)
{
var languageComboBox = ChangeLanguageCombo.SelectedItem;
LocalizationManager.ChangeAppLanguage(languageComboBox.ToString());
//Application.Current.Terminate(); I am commenting out because I don't neede to restart my app anymore.
}
else
{
SelectChoice();
}
}
private void SelectChoice()
{
//Select the saved language
string lang = LocalizationManager.GetCurrentAppLang();
if(lang == "bn-BD")
ChangeLanguageCombo.SelectedItem = ChangeLanguageCombo.Items[1];
else
{
ChangeLanguageCombo.SelectedItem = ChangeLanguageCombo.Items[0];
}
}
}
***Note: Before understanding what I did on LanguageSettings page's code behind, you must implement the codes from the link as stated earlier. And also it may be noted that I am working on windows phone 8

Winforms DateTimePicker set default text and/or value

I have a DateTimePicker with ShowCheckBox = true on my winforms app. If I do this in the forms constructor:
DateFrom.Checked = true;
DateFrom.Value = DateTime.Today.AddDays(-7);
Then set DateFrom.Checked = false; in the FormShown event, it does what I would like, the text in the control defaults to 7 days before today, and the checkbox is unchecked.
If I try to only set the Value, the text stays as today. If I reset Checked = false anytime before the FormShown event, the text stays as today.
Now I've moved this code to a user control, so to use the same "hack" will require even more hacking, so at this point I'm hoping someone has an easier method. Maybe just another property I can set besides from Value that actually works? :)
I tried this also:
DateFrom.Text = DateTime.Today.ToString(DateFrom.CustomFormat);
Instead of setting the value, or in addition to it, to no avail.
Typical, I tried for hours before posting my question, then right after I thought it might somehow be related to the creation of the window handle. So I came up with this solution. Will still be happy to have something better, but this doesn't seem to bad if I have to stay with this:
DateFrom.Checked = true;
DateFrom.Value = DateTime.Today.AddDays(-7);
if (DateFrom.Handle == null)
System.Threading.Thread.Sleep(0);
DateFrom.Checked = false;
Checking Handle forces the window handle to be created, so then I'm able to uncheck the control without it defaulting to today's date for the text when the window handle is created later. I just use Sleep(0) as a trick to make sure the compiler doesn't optimize the code and compile it out all together (not sure if that would even happen, but like to be sure, and condition shouldn't always be false so should never Sleep(0) anyway).
It might also be that the control is simply not redrawing properly, particularly if you have it as a usercontrol. You might try calling Invalidate() on the control after setting the Value to see if that's the problem.
I changed your self answer and made it like this:
public ProcessFailureForm()
{
InitializeComponent();
//Blah blah blah
dtFrom.HandleCreated += delegate //if you need sender or EventArgs use: delegate(object sender, EventArgs e)
{
dtFrom.Checked = true;
dtFrom.Value = DateTime.Today.AddDays(-7);
dtFrom.Checked = false;
};
}
Update:
Actually first i think like you, but after doing this, i tried and find out that the Checked state won't affect the process... so it can be reduced to:
public ProcessFailureForm()
{
InitializeComponent();
//Blah blah blah
dtFrom.HandleCreated += delegate //if you need sender or EventArgs use: delegate(object sender, EventArgs e)
{
dtFrom.Value = DateTime.Today.AddDays(-7);
};
}

Load previous session values when open new window

There is a MenuItem click event MainMenu_Define_Material which opens a window called Material. I get info from a textbox called txt_density and I save that info in density and return to main window via OK-button having click event Material_btn_OK_Click.
My question is, how I can avoid passing the last session values every time I open the window Material?
I want, once density is set, every time I open Material window I want to see the values of previous session.
private void MainMenu_Define_Material(object sender, RoutedEventArgs e)
{
newWin_material = new Material();
newWin_material.btn_OK.Click += new RoutedEventHandler(Material_btn_OK_Click);
if (density != -1)
{
newWin_material.txt_density.Text = density.ToString();
}
newWin_material.ShowDialog();
}
private void Material_btn_OK_Click(object sender, RoutedEventArgs e)
{
density = System.Convert.ToSingle(newWin_material.txt_density.Text);
newWin_material.Close();
}
Not very clear where that density comes from, but you can insert that field in uour data class what you can hold on data layer or may be like ObjectDataProvider for modelview.
There are a lor of different solutioms our there. The basic idea is:
Define common, shared data storage and keep there alll values you want to share between different windows.
Define a data class. I used here a static class with a static member Desnity.
public static class DataClass
{
public static double Desnsity;
}
After in any window you're able to access that field (read/write), like
DataClass.Density
Hope this is clear.
There is no concept of Session in WPF. You can try creating a static variable to do this or a app config value or pass a parameter via the constructor.
If you are going to new it ( e.g. new Material(); ) then it is going to start with zero information. That is what new does. What is wrong with passing the value in the ctor?
Try
MainWindow
{
private newWin_material = new Material(); // just new it ONCE
// this may need to be in MainWindow ctor.
newWin_material.btn_OK.Click += new RoutedEventHandler(Material_btn_OK_Click);

Does data binding work on invisible control?

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
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.
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;
}
}
}
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();
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.
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.
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.
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.

Categories

Resources